[{"data":1,"prerenderedAt":57878},["ShallowReactive",2],{"recent-blog-posts":3,"all-blog-posts-for-tags":3466},[4,798,1414,2053,2726,2857,3247,3323],{"id":5,"title":6,"body":7,"canonical":783,"date":784,"description":785,"extension":786,"featured":787,"image":788,"meta":789,"navigation":790,"ogimage":788,"path":791,"provider":788,"published":790,"seo":792,"stem":793,"tags":794,"url":788,"__hash__":797},"blog/blog/documenting-product-with-ai-agent.md","How I Documented an Entire Product in 4 Days with an AI Agent",{"type":8,"value":9,"toc":747},"minimark",[10,22,27,41,44,48,51,54,62,66,69,115,118,125,129,137,142,148,151,157,163,169,175,183,186,190,196,199,204,207,210,219,223,229,232,266,269,281,285,288,291,298,308,315,326,329,333,339,342,356,371,374,388,391,395,398,407,414,417,420,424,427,430,433,436,463,467,471,474,478,481,484,487,491,494,497,504,508,511,514,517,521,525,528,532,535,542,546,549,553,557,568,571,575,578,582,585,589,595,599,612,621,627,631,634,640,646,652,658,664,668,671,695,698,702,705,708,711,715,718,721,727,730,733,744],[11,12,13,14,21],"p",{},"I had 55 pages of documentation to write, 59 screenshots to capture, and a product that was still shipping features and being rebranded weeks before release. I did it in four days with ",[15,16,20],"a",{"href":17,"rel":18},"https://github.com/aaif-goose/goose",[19],"nofollow","Goose",", an open-source AI agent by Block, part of the Linux Foundation, and I want to walk you through exactly how. Not the polished version. The real one: how I built it, how it works, everything that broke along the way, and what I learned from it.",[23,24,26],"h2",{"id":25},"the-problem","The Problem",[11,28,29,34,35,40],{},[15,30,33],{"href":31,"rel":32},"https://theaiplatform.app",[19],"The AI Platform"," by ",[15,36,39],{"href":37,"rel":38},"https://zephyr-cloud.io",[19],"Zephyr Cloud"," is a desktop app where teams collaborate with AI specialists in channels. Think Slack meets AI agents. The product had been moving fast for months. Features were shipping, the UI was evolving, and the documentation was... not keeping up. What existed was a handful of developer-focused reference pages. Markdown files describing CRDT schemas and workflow adapter formats. Useful if you were building the product. Useless if you were trying to use it.",[11,42,43],{},"We needed end-user documentation. The kind where someone installs the app, opens the docs, and understands how to create a channel, mention a specialist, and get work done. And we needed it before the official release, which was a few weeks away.",[23,45,47],{"id":46},"why-an-ai-agent","Why an AI Agent",[11,49,50],{},"I have written plenty of documentation by hand. It is one of the most time-consuming parts of shipping a product. Not because the writing itself is hard, but because of everything around it. You need to understand the feature by reading source code. You need to take screenshots. You need to crop and optimize them. You need to keep the screenshots updated when the UI changes. You need to maintain consistent voice and structure across dozens of pages. And you need to do all of this while the product is still changing underneath you.",[11,52,53],{},"I had been using the agent for other tasks in the codebase and thought: what if I could create a way to write all the documentation from source code, capture screenshots that could be recaptured any time the app changes, and also improve the documentation based on those screenshots.",[11,55,56,57,61],{},"For those unfamiliar, Goose is an open-source AI agent that runs on your machine. It can read and write files, run shell commands, interact with APIs, and use extensions and ",[58,59,60],"strong",{},"skills"," to specialize in different tasks. Skills are markdown files that encode instructions, conventions, and tooling for a specific task. When you load a skill, the agent follows those instructions. When you improve the skill, every future session benefits. It is the difference between telling an agent what to do every time and teaching it once.",[23,63,65],{"id":64},"the-plan","The Plan",[11,67,68],{},"Before writing a single page, I sat down and created a phased plan. This turned out to be the most important decision of the whole project. You have an idea in your head but no real structure, and you need to think it through before throwing an agent at it. We created a tracer bullet format with sub-tasks so the agent could work phase by phase and tick off what it had done. One night I even went to bed and left it working on a task. The next morning I reviewed everything it had done and iterated over the parts that needed adjusting. I deliberately avoided using a loop where the agent just runs through everything unattended. I wanted to stay in charge and monitor how things were going, because I was also refining the skills as I went along.",[70,71,72,79,85,91,97,103,109],"ul",{},[73,74,75,78],"li",{},[58,76,77],{},"Phase 0: Restructure."," Delete developer-focused content from the user guide. Move reference docs to a separate section. Set up the directory structure.",[73,80,81,84],{},[58,82,83],{},"Phase 1: Getting Started."," Installation, account creation, platform tour, first channel. The first five minutes of the product.",[73,86,87,90],{},[58,88,89],{},"Phase 2: Daily Use."," Chat, messaging, threads, specialists. The features people use every day.",[73,92,93,96],{},[58,94,95],{},"Phase 3: Power Features."," Projects, tasks, workflows, knowledge garden. Features that experienced users reach for.",[73,98,99,102],{},[58,100,101],{},"Phase 4: Settings."," Connections, sandbox, MCP servers, billing, permissions, browser extensions. Every settings page documented.",[73,104,105,108],{},[58,106,107],{},"Phase 5: Polish."," Screenshots for all pages. Cross-linking. Consistent voice. Image optimization.",[73,110,111,114],{},[58,112,113],{},"Phase 6: Undocumented Features."," Go through the app screen by screen and find anything I missed. This phase caught the embedded browser, the code editor panel, and several settings pages that had no documentation at all.",[11,116,117],{},"The phased approach mattered because it gave me clear stopping points. After each phase, I could commit, review, and course-correct.",[11,119,120],{},[121,122],"img",{"alt":123,"src":124},"4-Day Sprint Timeline showing commit activity: Day 1 kickoff with 4 commits, Day 2 evening sprint with 12 commits, Day 3 with 43 commits including sidebar redesign disruption, Day 4 with 22 commits to finish and ship","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe06yzpcmpuljc4l5bg8y.png",[23,126,128],{"id":127},"the-three-skills-i-built","The Three Skills I Built",[11,130,131,132,136],{},"Here is where it gets interesting. I did not just use the agent to write documentation. I built three skills that taught it ",[133,134,135],"em",{},"how the documentation works",", and those skills evolved throughout the project as I hit problems and found better approaches.",[138,139,141],"h3",{"id":140},"_1-write-docs-the-style-guide-in-code","1. write-docs: The Style Guide in Code",[11,143,144],{},[121,145],{"alt":146,"src":147},"write-docs skill card: 513 lines covering voice and tone rules, page structure template, formatting conventions, and verification checklist","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3z0qbmo7s40ifp0x5vm.png",[11,149,150],{},"This skill is 513 lines of instructions that define how every documentation page should be written. It covers:",[11,152,153,156],{},[58,154,155],{},"Voice and tone."," Casual and friendly. Direct. Confident. \"Click Settings\" not \"You may want to consider clicking Settings.\"",[11,158,159,162],{},[58,160,161],{},"Formatting rules."," Bold for UI elements the user needs to find. Italics for text the user will see but not interact with. Code backticks for anything the user types. No emojis. No em dashes.",[11,164,165,168],{},[58,166,167],{},"Page structure."," Start with what the user sees, not how it works internally. One idea per paragraph. Lead with the action. A full page template with frontmatter, headings, screenshots, callouts, and cross-links.",[11,170,171,174],{},[58,172,173],{},"What not to document."," Internal implementation details, developer workflows, API references, features behind feature flags. This is user documentation, not a code tour.",[11,176,177,178,182],{},"The skill also includes a verification checklist that the agent walks through before committing. Content checks (no emojis, no em dashes, UI elements bolded), screenshot checks (optimized, cropped, registered in the manifest), and a build check (",[179,180,181],"code",{},"pnpm build"," must pass with no dead links). It is not an automated gate. It is instructions baked into the skill that the agent follows every time.",[11,184,185],{},"Why does this matter? Because without it, every documentation session would start with me re-explaining the same conventions. With the skill loaded, the agent writes in the right voice from the first sentence. And when I noticed a pattern I did not like (too many callouts per page, screenshots that were too large), I updated the skill once and every future page followed the new rule.",[138,187,189],{"id":188},"_2-doc-screenshots-automated-screenshot-capture","2. doc-screenshots: Automated Screenshot Capture",[11,191,192],{},[121,193],{"alt":194,"src":195},"doc-screenshots skill card: 478 lines of instructions plus 1,722 lines of tooling code, covering Peekaboo integration, Vision OCR, YAML manifest runner, and batch capture modes","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht3mwcraro7vuclm2i3u.png",[11,197,198],{},"This is the most technically interesting skill and the one that saved the most time. It is 478 lines of instructions backed by 1,722 lines of tooling code across four scripts: a bash CLI, a Python manifest runner, a Swift OCR text finder, and a Python highlight overlay renderer.",[200,201,203],"h4",{"id":202},"why-not-playwright","Why Not Playwright?",[11,205,206],{},"The first question people ask: why not use Playwright? I use Playwright every day. I love it. But it would not have worked here.",[11,208,209],{},"The AI Platform is a Tauri desktop app. The UI runs in a native webview, not a browser tab. Playwright automates browsers. It cannot connect to a Tauri webview. Even if you could somehow attach to the webview's DevTools protocol, you would be fighting against the native window chrome, the system title bar, and the fact that the app's routing and state management are wired through Tauri's IPC bridge, not standard browser navigation.",[11,211,212,213,218],{},"I needed something that works at the OS level: find the window, click things on screen, capture what the user actually sees. That led me to ",[15,214,217],{"href":215,"rel":216},"https://github.com/openclaw/Peekaboo",[19],"Peekaboo",", a macOS automation tool that interacts with apps through accessibility APIs and screen coordinates.",[200,220,222],{"id":221},"the-pipeline","The Pipeline",[11,224,225],{},[121,226],{"alt":227,"src":228},"Screenshot pipeline flow: Peekaboo navigates and focuses, Peekaboo --retina captures at 2x, Swift Vision OCR finds text, Pillow adds highlights, pngquant and optipng compress","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy4699jvgefweup3s6kkd.png",[11,230,231],{},"The pipeline works like this:",[70,233,234,239,248,254,260],{},[73,235,236,238],{},[58,237,217],{}," finds the app window and focuses it. If you need to navigate somewhere first, it clicks UI elements by their visible text.",[73,240,241,247],{},[58,242,243,244],{},"Peekaboo ",[179,245,246],{},"--retina"," captures the window at 2x retina resolution without the drop shadow.",[73,249,250,253],{},[58,251,252],{},"A Swift script using the Vision framework"," runs OCR on the captured image. It finds every piece of text and returns pixel-accurate bounding boxes.",[73,255,256,259],{},[58,257,258],{},"A Python script using Pillow"," draws highlight overlays, borders, and spotlight effects on the image based on the OCR results.",[73,261,262,265],{},[58,263,264],{},"pngquant and optipng"," compress the final image. This typically reduces file size by 50 to 60 percent with no visible quality loss.",[11,267,268],{},"No hardcoded coordinates for content elements. No browser automation. No authentication tokens. The agent looks at the actual app window, reads the text on screen, and figures out where things are.",[11,270,271,272,276,277,280],{},"The pipeline originally used three separate native macOS tools stitched together. I filed an issue on the ",[15,273,275],{"href":215,"rel":274},[19],"Peekaboo repo"," requesting retina capture support, and the maintainer shipped it within days. That simplified the pipeline to a single ",[179,278,279],{},"peekaboo image --retina"," call plus the Swift OCR script.",[200,282,284],{"id":283},"the-screen-takeover-problem","The Screen Takeover Problem",[11,286,287],{},"There is a real trade-off with this approach. Peekaboo needs the app window visible and in focus. While the audit or batch capture is running, it is clicking through your app, opening dialogs, navigating between pages, pressing Escape to close things. Your screen is not yours for the duration.",[11,289,290],{},"A full audit takes about 10 minutes. A full recapture takes 15 to 20. During that time, you cannot touch the mouse or keyboard without breaking the run. In practice, you kick off the batch, go make coffee, and come back to 59 freshly captured, cropped, and optimized screenshots. Captures can technically run in the background, but navigation clicks need the window in focus and control of the mouse. Even with a second monitor, if you move the mouse it interferes with the run. The agent needs your machine for the duration. Treat it as a coffee break. It is also not ready for CI yet since macOS CI runners do not have a logged-in GUI session with the Accessibility and Screen Recording permissions that Peekaboo needs.",[11,292,293,294,297],{},"The key insight was the ",[58,295,296],{},"screenshot manifest",". Instead of capturing screenshots one at a time, I defined all 59 of them in a YAML file:",[299,300,305],"pre",{"className":301,"code":303,"language":304},[302],"language-text","screenshots:\n  - id: getting-started/app-overview\n    output: docs/public/images/getting-started/app-overview.png\n    crop: window\n    description: >\n      Full app window showing the icon rail, channel list,\n      and a chat conversation.\n    validate:\n      - Channels\n\n  - id: getting-started/create-channel-dialog\n    output: docs/public/images/getting-started/create-channel-dialog.png\n    crop: main\n    steps:\n      - click: '+'\n        near: 'Channels'\n      - wait: 1.5\n    cleanup:\n      - press: 'Escape'\n\n","text",[179,306,303],{"__ignoreMap":307},"",[11,309,310,311,314],{},"Each entry declares what to capture, how to navigate there, what to crop, and what text should appear in the final image (the ",[179,312,313],{},"validate"," field). The manifest runner executes them in sequence, resetting the app state between each one.",[11,316,317,318,321,322,325],{},"The manifest means that when the UI changes, you do not retake screenshots by hand. You run the manifest and get all 59 back in one batch. An ",[179,319,320],{},"--audit"," mode walks every navigation step and reports which targets are broken. A ",[179,323,324],{},"--compare"," mode recaptures everything and saves new versions alongside the originals for side-by-side review.",[11,327,328],{},"I ran the audit while writing this blog post. 50 of 59 passed. Every failure was about test data that had changed (renamed channels, deleted workflows), not broken navigation. The core paths all still worked. The lesson: treat screenshot test data like E2E fixtures. Navigation screenshots are stable. Content-dependent ones need a dedicated docs workspace with controlled data.",[138,330,332],{"id":331},"_3-docs-preview-deploy-and-verify","3. docs-preview: Deploy and Verify",[11,334,335],{},[121,336],{"alt":337,"src":338},"docs-preview skill card: 155 lines covering Zephyr Cloud edge deploy, 3-second build cycle, URL management, and stale URL prevention","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4y9z3lp42qn5d5ykv2fm.png",[11,340,341],{},"The simplest skill, at 155 lines, but it solved two problems at once.",[11,343,344,347,348,351,352,355],{},[58,345,346],{},"Why not localhost?"," The documentation site builds with Rspress. You can run ",[179,349,350],{},"pnpm dev"," and preview on ",[179,353,354],{},"localhost:3000",", but that only works for you. You cannot share a localhost URL in a PR review, paste it into a Slack thread, or hand it to a teammate to check your work. I needed shareable URLs.",[11,357,358,359,362,363,367,368,370],{},"The docs build uses the ",[179,360,361],{},"withZephyr()"," Rspress plugin, which uploads the built site to ",[15,364,366],{"href":37,"rel":365},[19],"Zephyr Cloud's"," edge network on every ",[179,369,181],{},". The whole cycle takes under 2 seconds. Build, upload, deploy, live URL. I timed it while writing this post: 1.8 seconds for 55 pages and 59 images to go from source files to a production-ready URL on a global CDN.",[11,372,373],{},"That means every time the agent finishes writing or updating a page, it can build and hand me a live URL to check in the browser. No local server to start, no port conflicts, no \"works on my machine.\" Just a URL that anyone on the team can open.",[11,375,376,379,380,383,384,387],{},[58,377,378],{},"The URL problem."," Every build produces a unique URL with a hash suffix that changes each time. AI agents are bad at this. The URL has a fixed project number (like ",[179,381,382],{},"213",") and a per-build hash (like ",[179,385,386],{},"4a62f09db","). Before the skill existed, the agent would sometimes \"increment\" the project number thinking it was a build counter, or type a URL from memory with a fabricated hash. Both produce links that have never existed and always 404.",[11,389,390],{},"The skill stamps that out. It pipes the build output to a log file and re-greps the log whenever the URL is needed. It includes explicit warnings about not reusing stale URLs and not typing URLs from memory. Simple, but it eliminated a genuinely annoying class of failures.",[138,392,394],{"id":393},"verifying-the-docs-with-playwright-cli","Verifying the Docs With Playwright CLI",[11,396,397],{},"There is an important distinction in this workflow. Peekaboo automates the desktop app to capture screenshots. But who verifies that the documentation pages themselves render correctly?",[11,399,400,401,406],{},"That is where ",[15,402,405],{"href":403,"rel":404},"https://github.com/nichochar/playwright-cli",[19],"Playwright CLI"," comes in. It is a command-line tool that wraps Playwright's browser automation into simple terminal commands. The agent uses it to open the built documentation site in a real browser, take a DOM snapshot, and verify that headings and images rendered correctly.",[11,408,409,410,413],{},"The verification flow looks like this. After the agent writes a page, it runs ",[179,411,412],{},"playwright-cli snapshot"," to get the full DOM tree and checks that the H1 matches, all images loaded, the sidebar navigation includes the new page, and the table of contents lists the right H2 headings. If something is missing or broken, it fixes the page and rebuilds.",[11,415,416],{},"This matters because a build passing does not mean the page looks right. Rspress generates static HTML that hydrates with React, so a page can exist but render incorrectly if something is off in the markdown or frontmatter. Playwright actually loads the page in a browser engine and lets the agent inspect what a user would see. It catches dead images, broken navigation links, callouts that rendered as raw markdown instead of styled containers, and layout issues that only show up in the browser.",[11,418,419],{},"Two tools, two targets. Peekaboo verifies the app. Playwright CLI verifies the docs about the app.",[23,421,423],{"id":422},"working-with-the-agent-not-watching-it","Working With the Agent, Not Watching It",[11,425,426],{},"I want to be clear about something: this was not me kicking off an agent and walking away. It was a constant back-and-forth, like working with a colleague sitting right next to you.",[11,428,429],{},"Every page went through iteration. I would review what the agent wrote, point out what was wrong, ask for restructuring, and push back on phrasing. The getting started guide in particular went through several rounds of reworking. What is the right order to introduce features? Should installation come before the platform tour or after? How do you title a page so someone scanning the sidebar instantly knows what it covers? These are editorial decisions that an agent cannot make alone.",[11,431,432],{},"One technique that worked well was passing screenshots directly to the agent and saying \"check all the clickable items on this and document anything I missed.\" This shifted the process from documenting based on source code to documenting based on what a user actually sees. The agent could look at a screenshot, identify buttons, tabs, and menu items through OCR, cross-reference them with the existing docs, and flag the gaps. That is how I caught undocumented features like the embedded browser and the code editor panel in Phase 6.",[11,434,435],{},"The quality of what the agent produced was good first-draft material that needed editorial direction, not a rewrite. The voice was right because the skill defined it. The structure was right because the template enforced it. What I spent my time on was the higher-level decisions: how to organize the getting started flow, what to emphasize, what to cut, and making sure the documentation told a coherent story rather than just listing features.",[11,437,438,439,444,445,450,451,456,457,462],{},"You can see the output at ",[15,440,443],{"href":441,"rel":442},"https://docs.theaiplatform.app/",[19],"docs.theaiplatform.app",". The ",[15,446,449],{"href":447,"rel":448},"https://docs.theaiplatform.app/guide/getting-started/",[19],"Platform Tour"," shows the structure I landed on for the getting started flow. The ",[15,452,455],{"href":453,"rel":454},"https://docs.theaiplatform.app/guide/chat/",[19],"Chat section"," shows how a feature area breaks down into overview, channels, and messaging pages. The ",[15,458,461],{"href":459,"rel":460},"https://docs.theaiplatform.app/guide/settings/",[19],"Settings section"," shows the most straightforward pages where the structure was consistent enough that the agent could produce near-final drafts with minimal editing.",[23,464,466],{"id":465},"a-day-by-day-walkthrough","A Day-by-Day Walkthrough",[138,468,470],{"id":469},"day-1-the-kickoff","Day 1: The Kickoff",[11,472,473],{},"Day 1 was about the plan. I sat down and mapped out the phased approach: what to tackle in what order, how to break 55 pages into manageable batches, and what the agent would need to know before writing the first page. This was the most important work of the entire sprint. The product was also being rebranded, so I ran a rename pass across the existing documentation. Four commits. No new content yet, but the groundwork was laid.",[138,475,477],{"id":476},"day-2-the-evening-sprint","Day 2: The Evening Sprint",[11,479,480],{},"Phases 0 through 4 in a single evening. This sounds aggressive, and it was. But the phased plan made it possible. Each phase had a clear scope, and the agent could read the source code to understand each feature before writing about it.",[11,482,483],{},"The first commit kicked off Phase 0, which restructured everything, moving 6,769 lines of developer-focused content out of the user-facing docs. Then Phases 1 through 4 each produced a batch of pages with screenshots.",[11,485,486],{},"Twelve commits in about ninety minutes. All the scaffolding, all the content, all the initial screenshots. The quality was rough in places (I would fix that in later phases), but the coverage was there. Every major section of the product had at least a first-draft page.",[138,488,490],{"id":489},"day-3-the-real-work","Day 3: The Real Work",[11,492,493],{},"Day 3 had 43 commits. This is where the polish happened and where most of the problems surfaced.",[11,495,496],{},"Phase 5 started with adding missing screenshots and cross-links. Then the big disruption: the app's sidebar got redesigned mid-sprint. Text labels were replaced with an icon rail. Every screenshot showing the sidebar was wrong. Every navigation step clicking a text label was broken. The manifest paid for itself here. I updated the navigation steps, re-ran the batch, and had all 59 screenshots regenerated in minutes instead of retaking them by hand.",[11,498,499,500,503],{},"I also added ",[179,501,502],{},"reset"," steps to the manifest on day 3. Before each screenshot, the runner presses Escape twice and clicks the Chat icon to return to a known state. Without this, a failed screenshot left the app in a broken state that cascaded into every subsequent capture.",[138,505,507],{"id":506},"day-4-finish-and-ship","Day 4: Finish and Ship",[11,509,510],{},"Day 4 was Phase 6 (undocumented features) plus a thorough review pass. The embedded browser and code editor panels had no documentation at all. The agent read the source components, I opened the app to verify what the UI actually looked like, and wrote the pages together.",[11,512,513],{},"The review pass caught real issues: contradictory text on the account creation page, screenshots that were cropped too loosely, duplicate content between the workflows overview and the build-and-run page.",[11,515,516],{},"The final commit merged the PR: 55 documentation pages, 59 screenshots, and the three skills.",[23,518,520],{"id":519},"what-broke-along-the-way","What Broke Along the Way",[138,522,524],{"id":523},"the-rebrand","The Rebrand",[11,526,527],{},"The product was rebranded from Zephyr Agency to The AI Platform during the documentation sprint. The rename itself is mechanically simple (find and replace), but the follow-on work is not. Alt text on 59 screenshots. Config files. Every page referencing the product name. Sentences that started with the product name suddenly reading awkwardly with the article \"The\" prepended. This is not an agent problem. It is just the reality of documenting a product that is still evolving. But it added real friction to a sprint that was already moving fast.",[138,529,531],{"id":530},"ocr-is-not-perfect","OCR Is Not Perfect",[11,533,534],{},"The Vision framework's OCR is very good, but not flawless. It occasionally misreads text. \"Get update\" becomes \"Get undate.\" The letter \"I\" gets confused with \"l\" in certain fonts. When the agent tries to click \"Get update\" and OCR returns \"Get undate,\" the navigation step fails.",[11,536,537,538,541],{},"The workaround I built into the skill: search for a substring instead of the full text, use nearby anchor text to disambiguate, or fall back to coordinate-based clicking. The ",[179,539,540],{},"continue_on_failure"," flag on manifest steps lets non-critical navigation steps fail without aborting the entire screenshot.",[138,543,545],{"id":544},"tooltips-and-hover-states","Tooltips and Hover States",[11,547,548],{},"Moving the mouse to click an element sometimes triggers a tooltip that appears in the screenshot. The fix was straightforward once I understood it: move the cursor away from interactive elements before capturing. The script now does this automatically, but it cost me a round of retakes before I figured out what was happening.",[23,550,552],{"id":551},"what-worked-surprisingly-well","What Worked Surprisingly Well",[138,554,556],{"id":555},"skills-as-accumulated-knowledge","Skills as Accumulated Knowledge",[11,558,559,560,563,564,567],{},"The three skills started small and grew with every problem I hit. The ",[179,561,562],{},"doc-screenshots"," skill started as a wrapper around ",[179,565,566],{},"screencapture"," and Pillow. By the end, it had manifest batch processing, audit mode, validation, reset steps, coordinate-based fallbacks, card-level pixel scanning, and anti-tooltip cursor management.",[11,569,570],{},"Each improvement was triggered by a real problem. And because skills persist across sessions, the fix was permanent. The next time anyone on the team works on documentation, all of those fixes are already loaded.",[138,572,574],{"id":573},"the-manifest-as-a-screenshot-database","The Manifest as a Screenshot Database",[11,576,577],{},"Defining all 59 screenshots declaratively in YAML turned out to be the single most valuable technical decision. Not because batch capture is faster than individual capture (it is), but because it made screenshots a reproducible artifact. The sidebar redesign on day 3 proved it: update a width constant and a few navigation steps, run one command, and all 59 screenshots are regenerated. No manual retakes.",[138,579,581],{"id":580},"reading-source-code-for-accuracy","Reading Source Code for Accuracy",[11,583,584],{},"The agent reads the actual source code before writing documentation. When the docs said \"click the + button next to Channels,\" it was because the agent had found that button in the component tree, not because it was guessing. That said, source code is not always the final truth. The running app sometimes differs from what the code suggests. The skill instructs the agent to verify text against screenshots using OCR and update the docs when they do not match.",[23,586,588],{"id":587},"by-the-numbers","By the Numbers",[11,590,591],{},[121,592],{"alt":593,"src":594},"By the Numbers: 55 pages, 59 screenshots, 81 commits, 4-day sprint, 24K words, 3 skills built, 1 rebrand survived, 6.2 MB of images","https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtsnliu4dp8fl7agwv9t.png",[23,596,598],{"id":597},"what-i-would-do-differently","What I Would Do Differently",[11,600,601,604,605,608,609,611],{},[58,602,603],{},"Start the skills earlier."," The skills were created during the documentation sprint itself. If I had written even a rough version of the ",[179,606,607],{},"write-docs"," and ",[179,610,562],{}," skills before starting, the first day would have gone smoother. The early pages needed more revision because the conventions were not yet codified.",[11,613,614,617,618,620],{},[58,615,616],{},"Find a way to run screenshot audits in CI."," As mentioned above, the navigation clicks need a real display, so CI is not an option yet. But even running ",[179,619,320],{}," locally before merging a PR that touches the UI would catch most stale screenshots early.",[11,622,623,626],{},[58,624,625],{},"Write the manifest first, content second."," I wrote pages and captured screenshots as I went. It would have been faster to define the full manifest up front (just the navigation steps, no content), run it once to see what the app actually looks like everywhere, and then write the pages based on real screenshots instead of source code alone.",[23,628,630],{"id":629},"what-you-can-take-away","What You Can Take Away",[11,632,633],{},"If you are thinking about using an AI agent for documentation, here is what I think matters most.",[11,635,636,639],{},[58,637,638],{},"Teach the agent, do not just instruct it."," A prompt that says \"write documentation for this feature\" produces generic content. A skill that defines your voice, your formatting rules, your page structure, and your verification checklist produces documentation that sounds like your team wrote it. The upfront investment in the skill pays off on every subsequent page.",[11,641,642,645],{},[58,643,644],{},"Make screenshots reproducible."," Manual screenshots are the first thing that goes stale. A declarative manifest that can regenerate every screenshot in one command is worth the engineering effort. It changes screenshots from a one-time cost to a maintained artifact.",[11,647,648,651],{},[58,649,650],{},"Phase your work."," Even if you are using an agent, \"write all the docs\" is not a plan. Break it into phases with clear scope and clear deliverables. This gives you stopping points, review points, and the ability to course-correct.",[11,653,654,657],{},[58,655,656],{},"Expect things to break."," OCR will misread text. The UI will change mid-sprint. Preview URLs will go stale. The difference between a frustrating experience and a productive one is whether you encode the fix into a skill so it never happens again.",[11,659,660,663],{},[58,661,662],{},"Review everything."," The agent does not replace your judgment. It replaces the mechanical work. You still need to read every page, check every screenshot, and verify that the documentation matches what the user actually sees. The agent writes the first draft. You make it right.",[23,665,667],{"id":666},"making-docs-agent-ready","Making Docs Agent-Ready",[11,669,670],{},"Writing 55 pages for humans was only half the problem. Agents need to read documentation too.",[11,672,673,674,608,679,684,685,688,689,691,692,694],{},"I added ",[15,675,678],{"href":676,"rel":677},"https://docs.theaiplatform.app/llms.txt",[19],"llms.txt",[15,680,683],{"href":681,"rel":682},"https://docs.theaiplatform.app/llms-full.txt",[19],"llms-full.txt"," to the documentation site using the Rspress ",[179,686,687],{},"@rspress/plugin-llms"," plugin. The ",[179,690,678],{}," file is a structured index of every page with one-line descriptions. The ",[179,693,683],{}," file is the entire documentation site as a single 3,000-line markdown file that an agent can ingest in one request. Every page also has \"Copy as Markdown\" and \"Open in Claude\" buttons so users can feed specific pages to an LLM directly.",[11,696,697],{},"This is live now. Any agent that can fetch a URL can read the entire documentation in seconds.",[23,699,701],{"id":700},"automated-video-walkthroughs-work-in-progress","Automated Video Walkthroughs (Work in Progress)",[11,703,704],{},"Screenshots document a single state. But some features are easier to understand when you see them in motion. Creating a channel, mentioning a specialist, watching the response stream in. These are flows, not static screens.",[11,706,707],{},"I have a proof of concept for automated video walkthroughs using Peekaboo. The same manifest that defines screenshot navigation steps can drive a screen recording session: navigate to the starting point, start recording, walk through the steps, stop recording. The tooling exists in early form and produces usable results, but it is not production-ready yet. I am still working on consistent timing, smooth scrolling, and keeping the recordings tight enough to be useful without being rushed.",[11,709,710],{},"The goal is to embed these videos directly in the documentation pages so that when the UI changes, both screenshots and videos can be regenerated from the same manifest. That is not done yet, but the foundation is there.",[23,712,714],{"id":713},"the-future-documentation-in-an-agent-first-world","The Future: Documentation in an Agent-First World",[11,716,717],{},"Here is what I keep thinking about. I just spent four days writing 55 pages of documentation. It is good documentation. People will use it. But the way people use software is changing.",[11,719,720],{},"If you have a product with AI specialists built in, the product itself can guide you. Instead of leaving the app to read a documentation page about how to create a workflow, you ask the specialist in the app and it walks you through it. Instead of searching the docs for how to configure a setting, you describe what you want and the agent does it for you.",[11,722,723,724,726],{},"That does not mean documentation is dead. It means its role is shifting. Documentation becomes the knowledge layer that agents draw from. The ",[179,725,678],{}," work is a step in that direction. But the bigger shift is making the product itself so intuitive, with specialists that genuinely help, that fewer people need to leave the app to figure things out.",[11,728,729],{},"We are not there yet. Right now, the documentation is essential. But the future we are building toward is one where the product teaches you how to use it, and documentation exists as a reference layer for agents and for the edge cases that in-app guidance does not cover.",[731,732],"hr",{},[11,734,735,736,739,740,743],{},"The documentation is live at ",[15,737,443],{"href":441,"rel":738},[19],". If you want to try ",[15,741,33],{"href":31,"rel":742},[19],", it is available for macOS, Windows, and Linux.",[11,745,746],{},"And yes, this blog post was also created using Goose. It took about five hours of back-and-forth: pulling git history, running the audit and compare, timing preview builds, drafting sections, and then iterating step by step, redrafting, re-checking, and fixing everything until it was right. Agent-driven, not agent-written. Same process as the docs.",{"title":307,"searchDepth":748,"depth":748,"links":749},2,[750,751,752,753,760,761,767,772,777,778,779,780,781,782],{"id":25,"depth":748,"text":26},{"id":46,"depth":748,"text":47},{"id":64,"depth":748,"text":65},{"id":127,"depth":748,"text":128,"children":754},[755,757,758,759],{"id":140,"depth":756,"text":141},3,{"id":188,"depth":756,"text":189},{"id":331,"depth":756,"text":332},{"id":393,"depth":756,"text":394},{"id":422,"depth":748,"text":423},{"id":465,"depth":748,"text":466,"children":762},[763,764,765,766],{"id":469,"depth":756,"text":470},{"id":476,"depth":756,"text":477},{"id":489,"depth":756,"text":490},{"id":506,"depth":756,"text":507},{"id":519,"depth":748,"text":520,"children":768},[769,770,771],{"id":523,"depth":756,"text":524},{"id":530,"depth":756,"text":531},{"id":544,"depth":756,"text":545},{"id":551,"depth":748,"text":552,"children":773},[774,775,776],{"id":555,"depth":756,"text":556},{"id":573,"depth":756,"text":574},{"id":580,"depth":756,"text":581},{"id":587,"depth":748,"text":588},{"id":597,"depth":748,"text":598},{"id":629,"depth":748,"text":630},{"id":666,"depth":748,"text":667},{"id":700,"depth":748,"text":701},{"id":713,"depth":748,"text":714},"https://theaiplatform.app/blog/documenting-product-with-ai-agent/","2026-05-13","How I used Goose, an open-source AI agent, to write 55 pages of documentation, capture 59 screenshots, and build three reusable skills in just four days.","md",false,null,{},true,"/blog/documenting-product-with-ai-agent",{"title":6,"description":785},"blog/documenting-product-with-ai-agent",[795,796],"ai","webdev","4XLyHEwfbpHfSx9HZ63q193IRTx0tXPyl8EqVh_skr8",{"id":799,"title":800,"body":801,"canonical":1403,"date":1404,"description":1405,"extension":786,"featured":787,"image":788,"meta":1406,"navigation":790,"ogimage":788,"path":1407,"provider":788,"published":790,"seo":1408,"stem":1409,"tags":1410,"url":788,"__hash__":1413},"blog/blog/how-i-used-ai-to-fix-our-e2e-test-architecture.md","How I Used AI to Fix Our E2E Test Architecture",{"type":8,"value":802,"toc":1382},[803,806,809,816,820,823,842,845,849,852,859,862,928,931,935,938,966,970,976,980,983,1027,1049,1052,1056,1060,1081,1088,1106,1110,1113,1119,1126,1130,1140,1144,1147,1153,1159,1163,1166,1198,1202,1205,1208,1211,1215,1307,1311,1325,1329,1334,1354,1359],[11,804,805],{},"I joined a project with an existing Playwright E2E test suite, 38 spec files, ~165 tests, around 14,000 lines of test infrastructure. My first step was simple: run the tests locally.",[11,807,808],{},"8 out of 130 non-skipped tests passed. A 6% pass rate.",[11,810,811,812,815],{},"The confusing part? CI was green. It turned out CI ran everything with ",[179,813,814],{},"workers: 1",", multiple workers plus the dev environment meant running tests locally just wasn't possible.",[23,817,819],{"id":818},"step-1-analysis-asking-questions-i-didnt-know-the-answers-to","Step 1: Analysis — asking questions I didn't know the answers to",[11,821,822],{},"I had zero domain knowledge of this codebase. No context on why tests were written a certain way, what the custom wrappers did, or where the real problems were. So I started asking AI to analyze everything, the Playwright configs, the page objects, the spec files, the CI workflows. I asked questions to help me understand the codebase and to figure out what we could do to get tests running locally.",[11,824,825,826,829,830,829,833,829,836,608,839],{},"Over a few days, this produced 18 analysis documents covering ",[58,827,828],{},"Architecture",", ",[58,831,832],{},"Root causes",[58,834,835],{},"Anti-patterns",[58,837,838],{},"Silent bugs",[58,840,841],{},"Test isolation",[11,843,844],{},"The analysis phase was about building a map of a codebase I didn't understand. Every document was a question answered.",[23,846,848],{"id":847},"step-2-the-tracer-bullet-plan","Step 2: The tracer bullet plan",[11,850,851],{},"With the analysis done, I had a clear picture of what needed to change. But the question was: in what order, and how do you avoid a big refactor that breaks everything?",[11,853,854,855,858],{},"The answer was tracer bullets, a concept from ",[133,856,857],{},"The Pragmatic Programmer",". The idea is to build a thin end-to-end slice through all the layers to prove the architecture works, then expand from there.",[11,860,861],{},"I created 8 tracer bullets, each targeting a specific slice:",[70,863,864,870,876,882,892,898,912,918],{},[73,865,866,869],{},[58,867,868],{},"UI fixture chain"," — Use worker-scoped and test-scoped fixtures. Prove: fixtures work, teardown works, tests pass in CI.",[73,871,872,875],{},[58,873,874],{},"API fixture chain"," — Same pattern for API tests. Prove: composable fixtures work for API scenarios.",[73,877,878,881],{},[58,879,880],{},"Expand UI migrations"," — Apply the proven UI pattern to more files.",[73,883,884,887,888,891],{},[58,885,886],{},"MFE-scoped projects"," — Split one Playwright project into 7 projects by MFE folder (Applications, Organizations, Projects, etc.), each with ",[179,889,890],{},"dependencies: ['Setup']",".",[73,893,894,897],{},[58,895,896],{},"Teardown project"," — Add a cleanup project using Playwright's project dependencies.",[73,899,900,903,904,907,908,911],{},[58,901,902],{},"API fixture expansion"," — Composable API fixtures (",[179,905,906],{},"ownerOrg"," → ",[179,909,910],{},"ownerProject",").",[73,913,914,917],{},[58,915,916],{},"UI migration at scale"," — Remaining UI spec files.",[73,919,920,923,924,927],{},[58,921,922],{},"API setup project"," — Replace the no-op ",[179,925,926],{},"globalSetup"," with a proper setup project.",[11,929,930],{},"The key insight: the dependency graph told me which bullets could run in parallel. Bullets 1 and 2 were independent. Bullet 4 was independent. Bullet 3 depended on 1. This became important later when running multiple AI sessions.",[138,932,934],{"id":933},"what-a-tracer-bullet-looked-like-in-practice","What a tracer bullet looked like in practice",[11,936,937],{},"Bullet 1 targeted a single file with 5 tests. The steps:",[70,939,940,953,960,963],{},[73,941,942,943,907,946,907,949,952],{},"Add the fixture infrastructure (",[179,944,945],{},"currentUser",[179,947,948],{},"sharedOrg",[179,950,951],{},"project",")",[73,954,955,956,959],{},"Migrate ",[179,957,958],{},"projects-settings-general.spec.ts"," to use the fixtures",[73,961,962],{},"Run locally, verify tests pass",[73,964,965],{},"Push, verify CI is green",[23,967,969],{"id":968},"step-3-i-created-a-skill-to-do-the-work","Step 3: I created a skill to do the work",[11,971,972,973,891],{},"Once I had a plan with all 33 tasks organized into phases. I needed something to work through them consistently — same process every time, same quality bar, same benchmarking. So I built a skill: ",[179,974,975],{},"pw-test-improvement",[138,977,979],{"id":978},"what-the-skill-does","What the skill does",[11,981,982],{},"A strict 7-step process for every change:",[70,984,985,991,997,1003,1009,1015,1021],{},[73,986,987,990],{},[58,988,989],{},"Identify"," — Pick one item from the implementation tracker",[73,992,993,996],{},[58,994,995],{},"Baseline"," — Run the affected tests 3× before changes, record pass rate and timing",[73,998,999,1002],{},[58,1000,1001],{},"Fix"," — Apply the change following embedded Playwright best practices",[73,1004,1005,1008],{},[58,1006,1007],{},"Test"," — Run 3× after changes, all must pass",[73,1010,1011,1014],{},[58,1012,1013],{},"Compare"," — Document before/after benchmarks",[73,1016,1017,1020],{},[58,1018,1019],{},"Update"," — Mark the tracker item done",[73,1022,1023,1026],{},[58,1024,1025],{},"Commit"," — Only when asked, with a structured PR description",[11,1028,1029,1030,1033,1034,1033,1037,1040,1041,1044,1045,1048],{},"The skill had built-in knowledge: Playwright's locator priority (",[179,1031,1032],{},"getByRole"," > ",[179,1035,1036],{},"getByLabel",[179,1038,1039],{},"getByText"," > ...), a list of anti-patterns to avoid (",[179,1042,1043],{},"waitForTimeout",", no-op assertions, CSS class selectors, forced clicks without justification), and migration patterns for replacing the ",[179,1046,1047],{},"Actions"," wrapper with direct Playwright calls.",[11,1050,1051],{},"It used the Playwright CLI to run tests directly and capture results.",[23,1053,1055],{"id":1054},"the-architecture-changes","The architecture changes",[138,1057,1059],{"id":1058},"fixtures-replaced-boilerplate","Fixtures replaced boilerplate",[11,1061,1062,1063,1066,1067,1070,1071,829,1074,829,1077,1080],{},"The biggest change was moving from repeated ",[179,1064,1065],{},"beforeAll","/",[179,1068,1069],{},"afterAll"," blocks to Playwright fixtures. Before: each of 5 test files independently called ",[179,1072,1073],{},"getUser()",[179,1075,1076],{},"createOrg()",[179,1078,1079],{},"createProject()"," — 15 API calls total. After: worker-scoped fixtures shared across files — 7 calls total (53% reduction).",[11,1082,1083,1084,1087],{},"The key distinction was ",[58,1085,1086],{},"worker-scoped vs test-scoped",":",[70,1089,1090,1100],{},[73,1091,1092,1095,1096,1099],{},[58,1093,1094],{},"Worker-scoped"," (",[179,1097,1098],{},"{ scope: 'worker' }",") — created once, shared across all tests in that worker. Good for expensive setup like orgs and projects.",[73,1101,1102,1105],{},[58,1103,1104],{},"Test-scoped"," (default) — created fresh for each test. Good for data that tests mutate.",[138,1107,1109],{"id":1108},"project-structure","Project structure",[11,1111,1112],{},"The Playwright config went from one project running all 38 spec files to 7 projects, each pointing to its MFE folder:",[299,1114,1117],{"className":1115,"code":1116,"language":304},[302],"{ name: 'Applications', testDir: 'apps/ui/applications/e2e', dependencies: ['Setup'] },\n{ name: 'Organizations', testDir: 'apps/ui/organizations/e2e', dependencies: ['Setup'] },\n{ name: 'Projects', testDir: 'apps/ui/projects/e2e', dependencies: ['Setup'] },\n// ... Subscriptions, Host, User Profile\n",[179,1118,1116],{"__ignoreMap":307},[11,1120,1121,1122,1125],{},"This meant you could run ",[179,1123,1124],{},"--project=Applications"," to test just what you need, HTML reports grouped by area, and heavy specs got their own parallelism settings.",[138,1127,1129],{"id":1128},"the-serial-cascade-fix","The serial cascade fix",[11,1131,1132,1133,1136,1137,1139],{},"4 actual test failures looked like 57. Application tests used ",[179,1134,1135],{},"serial"," mode, so when the first test failed, all subsequent tests in that describe block were marked \"did not run.\" The fix: split heavy specs into a dedicated project, increase timeouts (30s → 60s for ",[179,1138,1065],{},"), cap workers to prevent API overload, and use worker-scoped fixtures to share expensive setup.",[23,1141,1143],{"id":1142},"what-went-wrong","What went wrong",[11,1145,1146],{},"Not everything worked first time.",[11,1148,1149,1152],{},[58,1150,1151],{},"The cleanup project broke CI."," We added a teardown project with Playwright's project dependencies to clean up test data after runs. It worked locally. In CI, it caused failures — the cleanup ran against a shared environment and interfered with other pipelines. Had to revert it.",[11,1154,1155,1158],{},[58,1156,1157],{},"Not everything should be a fixture."," We tried converting everything to fixtures. After reviewing Playwright docs, we rejected one of the fixtures before doing it as worker-scoped fixtures share across files, which would pollute serial tests that need per-file isolation with different options.",[23,1160,1162],{"id":1161},"how-i-worked-with-ai","How I worked with AI",[11,1164,1165],{},"This wasn't \"tell AI to fix it.\" It was a collaboration process:",[70,1167,1168,1174,1180,1186,1192],{},[73,1169,1170,1173],{},[58,1171,1172],{},"Ask questions relentlessly"," — \"What does this method do?\" \"Why is this test flaky?\" \"According to Playwright docs we can do X, can you verify your suggestion based on the docs\" I asked hundreds of questions during the analysis phase which lasted a few days.",[73,1175,1176,1179],{},[58,1177,1178],{},"Challenge every suggestion"," — \"Are you sure? What about edge case X?\" If the AI suggested a pattern, I'd ask it to explain why and if it was sure that was a good way of doing it.",[73,1181,1182,1185],{},[58,1183,1184],{},"Use docs as ground truth"," — I'd link to Playwright docs and ask \"does this align with what's in the docs?\" The AI's training data can be outdated; the docs are current.",[73,1187,1188,1191],{},[58,1189,1190],{},"Validate with multiple tools"," — I used Goose, Claude Code, and GitHub Copilot. Different tools catch different blind spots and have different opinions just like when you work with different team mates.",[73,1193,1194,1197],{},[58,1195,1196],{},"Check confidence explicitly"," — \"What's your confidence level on this? why only a 7? How can we get a 10 confidence level?\" This surfaces uncertainty the AI might not volunteer and also goes deeper to understanding what we haven't thought about and how we can improve things.",[138,1199,1201],{"id":1200},"running-it-in-practice","Running it in practice",[11,1203,1204],{},"I ran up to 4 AI sessions in parallel — based on which tracer bullets were independent of each other. The dependency graph from the implementation plan told me what could safely run at the same time.",[11,1206,1207],{},"I'd switch between sessions to check progress, read through what was being changed, and step in when something needed verifying. The AI did the mechanical work, applying patterns, running tests, capturing benchmarks. I did the oversight, deciding what to fix next, catching when a suggestion didn't look right, and verifying against the actual Playwright docs.",[11,1209,1210],{},"Never more than 4 at a time. I wanted to read and understand everything that was happening.",[23,1212,1214],{"id":1213},"what-we-measured","What we measured",[1216,1217,1218,1237],"table",{},[1219,1220,1221],"thead",{},[1222,1223,1224,1228,1231,1234],"tr",{},[1225,1226,1227],"th",{},"Metric",[1225,1229,1230],{},"Before",[1225,1232,1233],{},"After",[1225,1235,1236],{},"Change",[1238,1239,1240,1255,1269,1281,1294],"tbody",{},[1222,1241,1242,1246,1249,1252],{},[1243,1244,1245],"td",{},"API calls per file",[1243,1247,1248],{},"15",[1243,1250,1251],{},"7",[1243,1253,1254],{},"53% reduction",[1222,1256,1257,1260,1263,1266],{},[1243,1258,1259],{},"UI test setup lines",[1243,1261,1262],{},"8",[1243,1264,1265],{},"3",[1243,1267,1268],{},"62% reduction",[1222,1270,1271,1274,1276,1278],{},[1243,1272,1273],{},"API setup/cleanup lines",[1243,1275,1248],{},[1243,1277,1265],{},[1243,1279,1280],{},"80% reduction",[1222,1282,1283,1286,1288,1291],{},[1243,1284,1285],{},"Files with manual try/finally",[1243,1287,1248],{},[1243,1289,1290],{},"0",[1243,1292,1293],{},"Fixtures handle it",[1222,1295,1296,1299,1302,1304],{},[1243,1297,1298],{},"Boilerplate removed",[1243,1300,1301],{},"—",[1243,1303,1301],{},[1243,1305,1306],{},"~1,000 lines",[23,1308,1310],{"id":1309},"what-we-created-along-the-way","What we created along the way",[70,1312,1313,1316,1319,1322],{},[73,1314,1315],{},"18 analysis documents",[73,1317,1318],{},"5 implementation guides",[73,1320,1321],{},"33 tasks with verification commands",[73,1323,1324],{},"1 skill (test improvement)",[23,1326,1328],{"id":1327},"lessons-learned","Lessons learned",[11,1330,1331],{},[58,1332,1333],{},"About testing:",[70,1335,1336,1339,1342,1349],{},[73,1337,1338],{},"Green CI doesn't mean tests work locally",[73,1340,1341],{},"One real failure can cascade into dozens of phantom failures in serial mode",[73,1343,1344,1345,1348],{},"Web-first assertions (",[179,1346,1347],{},"expect(locator)",") catch timing issues that manual checks miss",[73,1350,1351,1352],{},"Fixtures aren't always the answer, some setup belongs in ",[179,1353,1065],{},[11,1355,1356],{},[58,1357,1358],{},"About working with AI:",[70,1360,1361,1364,1367,1370,1373,1376,1379],{},[73,1362,1363],{},"AI is better at applying known patterns than inventing new ones, give it a clear process",[73,1365,1366],{},"The analysis phase was the highest-leverage use of AI, it found things I'd have missed for weeks",[73,1368,1369],{},"Multiple tools > one tool, cross-checking catches hallucinations and enhances confidence in the approach",[73,1371,1372],{},"The skill made it scalable, without it, every fix would need the same instructions repeated",[73,1374,1375],{},"Keep the human in the loop, 4 parallel sessions, never unattended",[73,1377,1378],{},"Find the time to do these kind of tasks. They take time at first but then you achieve so much more.",[73,1380,1381],{},"Use AI just like it's a new colleague that you don't know very well who never turns on their camera so it's hard to get to know them and therefore you can't fully trust them but you know they have good opinions and are good at their job but you need to be sure they have thought things through and are not just being lazy and making bad decisions.",{"title":307,"searchDepth":748,"depth":748,"links":1383},[1384,1385,1388,1391,1396,1397,1400,1401,1402],{"id":818,"depth":748,"text":819},{"id":847,"depth":748,"text":848,"children":1386},[1387],{"id":933,"depth":756,"text":934},{"id":968,"depth":748,"text":969,"children":1389},[1390],{"id":978,"depth":756,"text":979},{"id":1054,"depth":748,"text":1055,"children":1392},[1393,1394,1395],{"id":1058,"depth":756,"text":1059},{"id":1108,"depth":756,"text":1109},{"id":1128,"depth":756,"text":1129},{"id":1142,"depth":748,"text":1143},{"id":1161,"depth":748,"text":1162,"children":1398},[1399],{"id":1200,"depth":756,"text":1201},{"id":1213,"depth":748,"text":1214},{"id":1309,"depth":748,"text":1310},{"id":1327,"depth":748,"text":1328},"https://dev.to/debs_obrien/how-i-used-ai-to-fix-our-e2e-test-architecture-444a","2026-04-29","AI-led analysis of a 6% local pass rate — from tracer bullet planning to building an agent skill that transformed 38 spec files with measurable benchmarks.",{},"/blog/how-i-used-ai-to-fix-our-e2e-test-architecture",{"title":800,"description":1405},"blog/how-i-used-ai-to-fix-our-e2e-test-architecture",[1411,1412,795],"testing","playwright","dL8Y5Bfq2QTXvPOUp7AkP6zZMCX9lNcigdIf9-VNtJs",{"id":1415,"title":1416,"body":1417,"canonical":788,"date":2045,"description":2046,"extension":786,"featured":787,"image":788,"meta":2047,"navigation":790,"ogimage":788,"path":2048,"provider":788,"published":790,"seo":2049,"stem":2050,"tags":2051,"url":788,"__hash__":2052},"blog/blog/claude-code-commands-and-shortcuts.md","Claude Code Commands and Shortcuts",{"type":8,"value":1418,"toc":2014},[1419,1430,1434,1441,1450,1457,1467,1470,1477,1483,1490,1495,1502,1508,1535,1541,1544,1551,1556,1563,1569,1576,1586,1590,1594,1601,1604,1608,1621,1653,1656,1660,1667,1674,1677,1681,1684,1691,1701,1712,1718,1728,1800,1811,1814,1818,1829,1833,1842,1848,1855,1859,1862,1882,1901,1977,1991,1994,2007,2010],[11,1420,1421,1422,1429],{},"When you first open Claude Code, it's not immediately obvious what commands are available to you. I spent some time today exploring the slash commands and keyboard shortcuts thanks to Matt Pocock's ",[15,1423,1426],{"href":1424,"rel":1425},"https://www.aihero.dev/cohorts/claude-code-for-real-engineers-2026-04",[19],[133,1427,1428],{},"Claude Code for Real Engineers"," course, and found them genuinely useful for day-to-day work. Here's a quick rundown of what each one does and when you might reach for it.",[23,1431,1433],{"id":1432},"slash-commands","Slash Commands",[138,1435,1437,1440],{"id":1436},"intro-setting-up-your-project-instructions",[179,1438,1439],{},"/intro"," - Setting Up Your Project Instructions",[11,1442,1443,1445,1446,1449],{},[179,1444,1439],{}," creates a ",[179,1447,1448],{},"claude.md"," file where you can define instructions for how Claude should behave in your project. If you're working in a team or want consistent responses across sessions, this is a good place to start.",[138,1451,1453,1456],{"id":1452},"terminal-setup-fixing-multi-line-input",[179,1454,1455],{},"/terminal-setup"," - Fixing Multi-Line Input",[11,1458,1459,1460,1462,1463,1466],{},"By default, hitting Enter sends your message immediately, which can be frustrating when you're trying to write something longer. ",[179,1461,1455],{}," configures your terminal so that ",[58,1464,1465],{},"Option + Enter"," (or Alt + Enter on Windows) gives you a new line instead.",[11,1468,1469],{},"One thing to note: you'll need to restart your terminal app after running this for the changes to take effect.",[138,1471,1473,1476],{"id":1472},"model-changing-the-default-model",[179,1474,1475],{},"/model"," - Changing the Default Model",[11,1478,1479,1480,1482],{},"If you want to switch which model Claude Code uses, ",[179,1481,1475],{}," lets you do that. Straightforward, but easy to miss if you don't know it's there.",[138,1484,1486,1489],{"id":1485},"usage-checking-your-subscription",[179,1487,1488],{},"/usage"," - Checking Your Subscription",[11,1491,1492,1494],{},[179,1493,1488],{}," shows your current usage for your subscription plan. Handy for keeping track of where you are without having to leave the terminal.",[138,1496,1498,1501],{"id":1497},"context-understanding-whats-in-your-context-window",[179,1499,1500],{},"/context"," - Understanding What's in Your Context Window",[11,1503,1504,1505,1507],{},"This one I found particularly useful. ",[179,1506,1500],{}," gives you a breakdown of what's currently loaded in your conversation, with estimated usage by category:",[70,1509,1510,1515,1520,1525,1530],{},[73,1511,1512],{},[58,1513,1514],{},"System prompts",[73,1516,1517],{},[58,1518,1519],{},"System tools",[73,1521,1522],{},[58,1523,1524],{},"Skills",[73,1526,1527],{},[58,1528,1529],{},"Messages",[73,1531,1532],{},[58,1533,1534],{},"Free space",[299,1536,1539],{"className":1537,"code":1538,"language":304},[302],"┌─────────────────────────────────────────────────────────────┐\n│  /context                                                   │\n│  └─ Context Usage                                           │\n│                                                             │\n│  claude-opus-4-6 · 15k/1000k tokens (1%)                   │\n│                                                             │\n│  Estimated usage by category                                │\n│  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   │\n│  ● System prompt:      5.6k tokens  (0.6%)                  │\n│  ● System tools:       8.3k tokens  (0.8%)                  │\n│  ○ Skills:              715 tokens  (0.1%)                   │\n│  ○ Messages:             58 tokens  (0.0%)                   │\n│  □ Free space:         952k tokens  (95.2%)                  │\n│  ■ Autocompact buffer:  33k tokens  (3.3%)                   │\n│                                                             │\n│  [████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 1%  │\n│   ^^^^                                                      │\n│   used                              free space              │\n└─────────────────────────────────────────────────────────────┘\n",[179,1540,1538],{"__ignoreMap":307},[11,1542,1543],{},"It also tells you when autocompaction will happen, that's when Claude automatically trims older context because the token limit is running low. If you've ever wondered why Claude seems to \"forget\" something from earlier in a long session, this command helps explain what's going on.",[138,1545,1547,1550],{"id":1546},"clear-starting-fresh",[179,1548,1549],{},"/clear"," - Starting Fresh",[11,1552,1553,1555],{},[179,1554,1549],{}," wipes your chat history and context window. It's essentially the same as closing and starting a new Claude session. Useful when you're switching to a completely different task and don't need the previous context hanging around.",[138,1557,1559,1562],{"id":1558},"ide-connect-to-your-ide",[179,1560,1561],{},"/ide"," - Connect to Your IDE",[11,1564,1565,1566,1568],{},"There's a Claude Code extension for VS Code, and you can connect to it by running ",[179,1567,1561],{},". Once connected, things like git diffs will open in VS Code instead of displaying in the terminal. If you're reviewing changes regularly this is a much better experience, you get proper syntax highlighting and the familiar side-by-side diff view rather than trying to read through diffs in the terminal.",[138,1570,1572,1575],{"id":1571},"resume-browse-previous-sessions",[179,1573,1574],{},"/resume"," - Browse Previous Sessions",[11,1577,1578,1579,1581,1582,1585],{},"Type ",[179,1580,1574],{}," and use the ",[58,1583,1584],{},"up and down arrow keys"," to browse through your previous sessions. There's also a search box so you can find a specific session across all sessions in the repo.",[23,1587,1589],{"id":1588},"tips","Tips",[138,1591,1593],{"id":1592},"interrupting-claude-with-escape","Interrupting Claude with Escape",[11,1595,1596,1597,1600],{},"Press ",[58,1598,1599],{},"Escape"," at any time to interrupt Claude while it's generating a response. If you want it to continue from where it left off, just type \"go.\" Press Escape again if you want to stop it for good.",[11,1602,1603],{},"This is helpful when you realise partway through that you need to rephrase your question or Claude is heading in the wrong direction.",[138,1605,1607],{"id":1606},"rewind-with-escape-escape","Rewind with Escape + Escape",[11,1609,1596,1610,1612,1613,1616,1617,1620],{},[58,1611,1599],{}," twice to enter rewind mode. This lets you scroll back through your conversation using the ",[58,1614,1615],{},"up arrow key",". When you land on the point you want to go back to, press ",[58,1618,1619],{},"Enter"," and you'll get a few options:",[70,1622,1623,1629,1635,1641,1647],{},[73,1624,1625,1628],{},[58,1626,1627],{},"Restore code and conversation"," - rolls back both your files and the chat",[73,1630,1631,1634],{},[58,1632,1633],{},"Restore conversation"," - rewinds the chat but keeps your code as-is",[73,1636,1637,1640],{},[58,1638,1639],{},"Restore code"," - reverts your files but keeps the conversation",[73,1642,1643,1646],{},[58,1644,1645],{},"Summarize from here"," - condenses everything from that point forward",[73,1648,1649,1652],{},[58,1650,1651],{},"Never mind"," - cancels and takes you back to where you were",[11,1654,1655],{},"This is really useful when Claude has gone down the wrong path and you want to undo a series of changes without manually reverting files yourself.",[138,1657,1659],{"id":1658},"stash-your-prompt-with-ctrl-s","Stash Your Prompt with Ctrl + S",[11,1661,1662,1663,1666],{},"This one would have saved me a lot of time if I'd known about it sooner. If you're mid-way through typing a prompt and realise you need to ask something else first, press ",[58,1664,1665],{},"Ctrl + S"," to stash it. Your current prompt gets set aside, you can type and submit something else, and then the stashed prompt automatically restores in the input field, ready for you to send or stash again.",[11,1668,1669,1670,1673],{},"If you decide you no longer need the stashed prompt, just press ",[58,1671,1672],{},"Ctrl + C"," to get rid of it.",[11,1675,1676],{},"Before I knew this existed, I was copying my prompt to the clipboard, typing the other thing, and then pasting it back in. Not the end of the world, but once you've done that a few times in a session it gets old fast.",[138,1678,1680],{"id":1679},"paste-images-directly-into-claude-code","Paste Images Directly into Claude Code",[11,1682,1683],{},"Something I didn't expect from a terminal-based tool: you can copy and paste images right into Claude Code. Just copy an image and paste it into the input field, then ask questions about it. Useful for things like sharing a screenshot of an error and asking what's wrong, pasting a design mockup and asking Claude to build it, or getting help interpreting a diagram or chart.",[138,1685,1687,1688],{"id":1686},"bash-mode-with","Bash Mode with ",[179,1689,1690],{},"!",[11,1692,1693,1694,1696,1697,1700],{},"Prefix any input with ",[179,1695,1690],{}," to run it as a bash command directly from Claude Code. For example, ",[179,1698,1699],{},"!npm run typecheck"," will run your typecheck and show the output. The useful part here is that any error messages from those commands are now in Claude's context, so you can immediately ask it to help fix whatever went wrong.",[11,1702,1703,1704,1707,1708,1711],{},"You can also run long-running processes like ",[179,1705,1706],{},"!npm run dev"," and then press ",[58,1709,1710],{},"Ctrl + B"," to send it to the background. You'll see a message like:",[1713,1714,1715],"blockquote",{},[11,1716,1717],{},"Command was manually backgrounded by user with ID: be96u9i91. Output is being written...",[11,1719,1720,1721,1724,1725,1727],{},"A background task indicator will appear, and you can use the ",[58,1722,1723],{},"arrow keys"," to navigate to it and press ",[58,1726,1619],{}," to view the shell details:",[299,1729,1733],{"className":1730,"code":1731,"language":1732,"meta":307,"style":307},"language-console shiki shiki-themes github-light github-dark","Shell details\n\nStatus:  running\nRuntime: 2m 15s\nCommand: npm run dev\n\nOutput:\n> dev\n> react-router dev\n  ➜  Local:   http://localhost:5173/\n  ➜  Network: use --host to expose\n","console",[179,1734,1735,1743,1748,1753,1759,1765,1770,1776,1782,1788,1794],{"__ignoreMap":307},[1736,1737,1740],"span",{"class":1738,"line":1739},"line",1,[1736,1741,1742],{},"Shell details\n",[1736,1744,1745],{"class":1738,"line":748},[1736,1746,1747],{"emptyLinePlaceholder":790},"\n",[1736,1749,1750],{"class":1738,"line":756},[1736,1751,1752],{},"Status:  running\n",[1736,1754,1756],{"class":1738,"line":1755},4,[1736,1757,1758],{},"Runtime: 2m 15s\n",[1736,1760,1762],{"class":1738,"line":1761},5,[1736,1763,1764],{},"Command: npm run dev\n",[1736,1766,1768],{"class":1738,"line":1767},6,[1736,1769,1747],{"emptyLinePlaceholder":790},[1736,1771,1773],{"class":1738,"line":1772},7,[1736,1774,1775],{},"Output:\n",[1736,1777,1779],{"class":1738,"line":1778},8,[1736,1780,1781],{},"> dev\n",[1736,1783,1785],{"class":1738,"line":1784},9,[1736,1786,1787],{},"> react-router dev\n",[1736,1789,1791],{"class":1738,"line":1790},10,[1736,1792,1793],{},"  ➜  Local:   http://localhost:5173/\n",[1736,1795,1797],{"class":1738,"line":1796},11,[1736,1798,1799],{},"  ➜  Network: use --host to expose\n",[11,1801,1802,1803,1806,1807,1810],{},"From the shell details view, you can press ",[58,1804,1805],{},"X"," to stop the background process, or press the ",[58,1808,1809],{},"left arrow key"," to go back to your conversation.",[11,1812,1813],{},"This means you can keep your dev server running in the background while continuing to work with Claude in the foreground. Because the output is being captured, Claude can see what's happening with the process so if something crashes or throws an error, it already has that context and can help you debug it.",[138,1815,1817],{"id":1816},"suspend-claude-with-ctrl-z","Suspend Claude with Ctrl + Z",[11,1819,1820,1821,1824,1825,1828],{},"If you need to run a bash command outside of Claude, something you don't want in its context, press ",[58,1822,1823],{},"Ctrl + Z"," to suspend the process. Run whatever you need to in your terminal, then type ",[179,1826,1827],{},"fg"," to bring Claude back. Handy for things like checking credentials, running unrelated scripts, or anything you'd rather keep out of the conversation.",[138,1830,1832],{"id":1831},"ending-and-resuming-sessions","Ending and Resuming Sessions",[11,1834,1596,1835,1837,1838,1841],{},[58,1836,1672],{}," twice to end your current session. Claude persists sessions locally, so when you exit it gives you a command to resume that session, something like ",[179,1839,1840],{},"claude --resume \u003Csession-id>",". Just copy and paste it to pick up where you left off.",[11,1843,1844,1845,1847],{},"If you've already closed the session and didn't save the command, no problem. Open Claude Code and use the ",[179,1846,1574],{}," slash command to browse your history.",[11,1849,1850,1851,1854],{},"If you just want to jump straight back into your most recent session, ",[179,1852,1853],{},"claude --continue"," does exactly that.",[138,1856,1858],{"id":1857},"managing-permissions","Managing Permissions",[11,1860,1861],{},"When Claude needs to run something, it will ask for permission with a few options:",[70,1863,1864,1870,1876],{},[73,1865,1866,1869],{},[58,1867,1868],{},"Yes"," - allow it this once",[73,1871,1872,1875],{},[58,1873,1874],{},"Yes, and don't ask again for..."," - allow it going forward",[73,1877,1878,1881],{},[58,1879,1880],{},"No"," - block it, with the option to give a reason or suggest a different command",[11,1883,1884,1885,1888,1889,1892,1893,1896,1897,1900],{},"These choices are saved to a file called ",[179,1886,1887],{},"settings.local.json"," inside the ",[179,1890,1891],{},".claude"," folder in your project. Inside that file you'll find a ",[179,1894,1895],{},"permissions"," property with an ",[179,1898,1899],{},"allow"," array listing everything you've approved. You can edit this manually to add commands, for example:",[299,1902,1906],{"className":1903,"code":1904,"language":1905,"meta":307,"style":307},"language-json shiki shiki-themes github-light github-dark","{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(pnpm typecheck)\",\n      \"Bash(pnpm *)\"\n    ],\n    \"deny\": [\n      \"Bash(git push *)\"\n    ]\n  }\n}\n","json",[179,1907,1908,1914,1923,1931,1940,1945,1950,1957,1962,1967,1972],{"__ignoreMap":307},[1736,1909,1910],{"class":1738,"line":1739},[1736,1911,1913],{"class":1912},"sVt8B","{\n",[1736,1915,1916,1920],{"class":1738,"line":748},[1736,1917,1919],{"class":1918},"sj4cs","  \"permissions\"",[1736,1921,1922],{"class":1912},": {\n",[1736,1924,1925,1928],{"class":1738,"line":756},[1736,1926,1927],{"class":1918},"    \"allow\"",[1736,1929,1930],{"class":1912},": [\n",[1736,1932,1933,1937],{"class":1738,"line":1755},[1736,1934,1936],{"class":1935},"sZZnC","      \"Bash(pnpm typecheck)\"",[1736,1938,1939],{"class":1912},",\n",[1736,1941,1942],{"class":1738,"line":1761},[1736,1943,1944],{"class":1935},"      \"Bash(pnpm *)\"\n",[1736,1946,1947],{"class":1738,"line":1767},[1736,1948,1949],{"class":1912},"    ],\n",[1736,1951,1952,1955],{"class":1738,"line":1772},[1736,1953,1954],{"class":1918},"    \"deny\"",[1736,1956,1930],{"class":1912},[1736,1958,1959],{"class":1738,"line":1778},[1736,1960,1961],{"class":1935},"      \"Bash(git push *)\"\n",[1736,1963,1964],{"class":1738,"line":1784},[1736,1965,1966],{"class":1912},"    ]\n",[1736,1968,1969],{"class":1738,"line":1790},[1736,1970,1971],{"class":1912},"  }\n",[1736,1973,1974],{"class":1738,"line":1796},[1736,1975,1976],{"class":1912},"}\n",[11,1978,1979,1980,1983,1984,1987,1988,891],{},"Use wildcards to allow a range of commands—",[179,1981,1982],{},"Bash(pnpm *)"," will permit any pnpm command. Use ",[179,1985,1986],{},"deny"," to explicitly block things you never want Claude to run, like ",[179,1989,1990],{},"Bash(git push *)",[11,1992,1993],{},"Permissions aren't limited to bash commands either, they also cover things like web search and other tools.",[11,1995,1996,1997,1999,2000,2003,2004,891],{},"By default, ",[179,1998,1887],{}," is ignored via ",[179,2001,2002],{},".gitignore"," so your permissions stay local to your machine. If you want to share them with your team, rename the file to ",[179,2005,2006],{},"settings.json",[11,2008,2009],{},"Hope this helps you move faster with Claude. Have fun.",[2011,2012,2013],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":2015},[2016,2034],{"id":1432,"depth":748,"text":1433,"children":2017},[2018,2020,2022,2024,2026,2028,2030,2032],{"id":1436,"depth":756,"text":2019},"/intro - Setting Up Your Project Instructions",{"id":1452,"depth":756,"text":2021},"/terminal-setup - Fixing Multi-Line Input",{"id":1472,"depth":756,"text":2023},"/model - Changing the Default Model",{"id":1485,"depth":756,"text":2025},"/usage - Checking Your Subscription",{"id":1497,"depth":756,"text":2027},"/context - Understanding What's in Your Context Window",{"id":1546,"depth":756,"text":2029},"/clear - Starting Fresh",{"id":1558,"depth":756,"text":2031},"/ide - Connect to Your IDE",{"id":1571,"depth":756,"text":2033},"/resume - Browse Previous Sessions",{"id":1588,"depth":748,"text":1589,"children":2035},[2036,2037,2038,2039,2040,2042,2043,2044],{"id":1592,"depth":756,"text":1593},{"id":1606,"depth":756,"text":1607},{"id":1658,"depth":756,"text":1659},{"id":1679,"depth":756,"text":1680},{"id":1686,"depth":756,"text":2041},"Bash Mode with !",{"id":1816,"depth":756,"text":1817},{"id":1831,"depth":756,"text":1832},{"id":1857,"depth":756,"text":1858},"2026-03-31","A guide to Claude Code's slash commands, keyboard shortcuts, and tips for a smoother workflow in the terminal.",{},"/blog/claude-code-commands-and-shortcuts",{"title":1416,"description":2046},"blog/claude-code-commands-and-shortcuts",[795],"bXGuulY-edl5X5j5s6itSoy2yFxAGzOGV1j42UPvC-U",{"id":2054,"title":2055,"body":2056,"canonical":788,"date":2717,"description":2718,"extension":786,"featured":787,"image":788,"meta":2719,"navigation":790,"ogimage":788,"path":2720,"provider":788,"published":790,"seo":2721,"stem":2722,"tags":2723,"url":788,"__hash__":2725},"blog/blog/built-agent-skill-readme-wizard.md","I Built an Agent Skill That Turns Rough READMEs Into Polished Docs",{"type":8,"value":2057,"toc":2703},[2058,2061,2067,2070,2073,2076,2079,2085,2089,2092,2095,2098,2118,2121,2124,2128,2131,2142,2162,2165,2168,2171,2174,2178,2181,2184,2187,2193,2213,2216,2219,2222,2225,2229,2232,2235,2241,2244,2250,2256,2259,2280,2283,2286,2292,2408,2411,2415,2418,2425,2451,2454,2457,2461,2464,2470,2488,2491,2494,2498,2501,2504,2511,2525,2528,2531,2548,2551,2555,2558,2561,2564,2567,2570,2573,2576,2579,2599,2602,2605,2608,2612,2615,2640,2643,2646,2650,2653,2660,2663,2685,2688,2694,2697,2700],[11,2059,2060],{},"If you're new to agent skills, start with my beginner guide first:",[11,2062,2063],{},[15,2064,2066],{"href":2065},"/blog/what-are-agent-skills-beginners-guide","What Are Agent Skills? Beginners Guide",[11,2068,2069],{},"That post covers what skills are, how they get loaded, and how to build a tiny one from scratch.",[11,2071,2072],{},"This post picks up where that one stops.",[11,2074,2075],{},"Instead of another tiny example, I want to show you what a practical skill looks like when it solves a real problem.",[11,2077,2078],{},"We are going to take the idea of a skill and use it to turn rough project READMEs into polished docs that are consistent, accurate, and reusable across repos. I picked README generation because the output is easy to judge, it comes up again and again, and once you get it right for one project you want the same quality bar everywhere.",[11,2080,2081],{},[121,2082],{"alt":2083,"src":2084},"Before vs After","https://raw.githubusercontent.com/debs-obrien/learn-agent-skills/main/assets/before-vs-after.png",[23,2086,2088],{"id":2087},"the-problem-with-one-off-readme-prompts","The problem with one-off README prompts",[11,2090,2091],{},"You can absolutely ask an agent to improve your README and get something decent back.",[11,2093,2094],{},"Sometimes it will even be very good.",[11,2096,2097],{},"But if you do that across multiple projects, the cracks show up quickly:",[70,2099,2100,2103,2106,2109,2112,2115],{},[73,2101,2102],{},"badge styles are inconsistent",[73,2104,2105],{},"section order changes from repo to repo",[73,2107,2108],{},"install commands drift away from the actual package manager",[73,2110,2111],{},"social links get guessed",[73,2113,2114],{},"simple projects end up with bloated READMEs",[73,2116,2117],{},"the agent repeats the same repo-scanning work every time",[11,2119,2120],{},"That is exactly the kind of problem skills are good at solving.",[11,2122,2123],{},"Not because they magically make the model smarter, but because they turn a vague prompt into a reusable workflow.",[23,2125,2127],{"id":2126},"the-first-version-was-just-one-file","The first version was just one file",[11,2129,2130],{},"I did not start with a big architecture.",[11,2132,2133,2134,2137,2138,2141],{},"The first version of ",[179,2135,2136],{},"readme-wizard"," was just a single ",[179,2139,2140],{},"SKILL.md"," with instructions telling the agent to:",[70,2143,2144,2147,2150,2153,2156,2159],{},[73,2145,2146],{},"detect the project name, description, license, git remote, package manager, and CI setup",[73,2148,2149],{},"add a better structure to the README",[73,2151,2152],{},"use shields.io badges",[73,2154,2155],{},"include a Quick Start section with real commands",[73,2157,2158],{},"show a project structure tree",[73,2160,2161],{},"add contributor avatars, documentation links, and optional social badges",[11,2163,2164],{},"That first version worked.",[11,2166,2167],{},"And that matters.",[11,2169,2170],{},"One of the easiest mistakes to make with agent workflows is over-engineering too early. A single file is often enough to prove whether the workflow is useful before you invest more time into it.",[11,2172,2173],{},"Here is the important part: start with the smallest thing that can produce a useful result on a real project.",[23,2175,2177],{"id":2176},"what-broke-in-practice","What broke in practice",[11,2179,2180],{},"Once I started testing the skill on real repos, the limitations showed up quickly.",[11,2182,2183],{},"The main issue was not that the agent could not write a README. It could.",[11,2185,2186],{},"The issue was consistency.",[11,2188,2189,2190,2192],{},"The single-file version was asking the ",[179,2191,2140],{}," to do too many jobs at once:",[70,2194,2195,2198,2201,2204,2207,2210],{},[73,2196,2197],{},"writing guidance",[73,2199,2200],{},"badge formats",[73,2202,2203],{},"project-type adaptation rules",[73,2205,2206],{},"README structure templates",[73,2208,2209],{},"Mermaid diagram templates",[73,2211,2212],{},"instructions for how to detect project metadata",[11,2214,2215],{},"That creates a few problems.",[11,2217,2218],{},"First, the file gets bloated fast. By the time I had all those rules and templates inline, it was over 150 lines and hard to maintain.",[11,2220,2221],{},"Second, the agent had to figure out how to inspect the repo on every single run. There was no scanning script yet — just instructions saying \"detect the package manager, find the license, parse the git remote.\" The agent would improvise that detection work each time. Sometimes it got it right. Sometimes it missed a CI workflow file, guessed at the wrong package manager, or invented social links that did not exist.",[11,2223,2224],{},"Third, all of that detection reasoning burned tokens and produced inconsistent results. The kind of work that should be boring and repeatable was instead fuzzy and error-prone.",[23,2226,2228],{"id":2227},"the-turning-point-treat-the-skill-like-a-workflow-not-a-prompt","The turning point: treat the skill like a workflow, not a prompt",[11,2230,2231],{},"That was the point where the skill stopped being just a better prompt and started becoming a real workflow.",[11,2233,2234],{},"The structure ended up looking like this:",[299,2236,2239],{"className":2237,"code":2238,"language":304,"meta":307},[302],".agents/skills/readme-wizard/\n├── SKILL.md\n├── scripts/\n│   └── scan_project.sh\n├── references/\n│   └── readme-best-practices.md\n├── assets/\n│   ├── badges.json\n│   ├── diagrams.md\n│   └── readme-template.md\n└── evals/\n    └── evals.json\n",[179,2240,2238],{"__ignoreMap":307},[11,2242,2243],{},"Every part has a different job. And that is the point.",[23,2245,2247,2249],{"id":2246},"skillmd-became-the-orchestrator",[179,2248,2140],{}," became the orchestrator",[11,2251,2252,2253,2255],{},"Instead of being one giant wall of instructions, ",[179,2254,2140],{}," became the thin coordinator.",[11,2257,2258],{},"Its job is to define the workflow:",[2260,2261,2262,2265,2268,2271,2274,2277],"ol",{},[73,2263,2264],{},"run the scan script",[73,2266,2267],{},"read the README best-practices guide",[73,2269,2270],{},"build from the template",[73,2272,2273],{},"pull badge formats from the badge catalog",[73,2275,2276],{},"validate against the eval assertions",[73,2278,2279],{},"only load diagram templates if the project actually needs them",[11,2281,2282],{},"That is a much better use of the main skill file.",[11,2284,2285],{},"It keeps the top-level instructions focused on sequence and judgment instead of burying everything in one place.",[11,2287,2288,2289,2291],{},"Here is what the workflow section of the final ",[179,2290,2140],{}," looks like:",[299,2293,2297],{"className":2294,"code":2295,"language":2296,"meta":307,"style":307},"language-markdown shiki shiki-themes github-light github-dark","## Workflow\n\n### 1. Scan the project\nRun `scripts/scan_project.sh \u003Cproject-directory>` to collect structured JSON metadata.\n\n### 2. Read the best practices guide\nRead `references/readme-best-practices.md` before writing.\n\n### 3. Build the README\nUse `assets/readme-template.md` as the base structure.\nReplace {{PLACEHOLDER}} markers with actual project data from the scan.\n\n### 4. Add badges\nRead `assets/badges.json` for the full badge catalog.\nOnly include badges for things that actually exist.\n\n### 5. Validate the output\nReview the generated README against the assertions in `evals/evals.json`.\n\n### 6. Optionally add a diagram\nOnly read `assets/diagrams.md` if the project has multiple components.\n","markdown",[179,2298,2299,2304,2308,2313,2318,2322,2327,2332,2336,2341,2346,2351,2356,2362,2368,2374,2379,2385,2391,2396,2402],{"__ignoreMap":307},[1736,2300,2301],{"class":1738,"line":1739},[1736,2302,2303],{},"## Workflow\n",[1736,2305,2306],{"class":1738,"line":748},[1736,2307,1747],{"emptyLinePlaceholder":790},[1736,2309,2310],{"class":1738,"line":756},[1736,2311,2312],{},"### 1. Scan the project\n",[1736,2314,2315],{"class":1738,"line":1755},[1736,2316,2317],{},"Run `scripts/scan_project.sh \u003Cproject-directory>` to collect structured JSON metadata.\n",[1736,2319,2320],{"class":1738,"line":1761},[1736,2321,1747],{"emptyLinePlaceholder":790},[1736,2323,2324],{"class":1738,"line":1767},[1736,2325,2326],{},"### 2. Read the best practices guide\n",[1736,2328,2329],{"class":1738,"line":1772},[1736,2330,2331],{},"Read `references/readme-best-practices.md` before writing.\n",[1736,2333,2334],{"class":1738,"line":1778},[1736,2335,1747],{"emptyLinePlaceholder":790},[1736,2337,2338],{"class":1738,"line":1784},[1736,2339,2340],{},"### 3. Build the README\n",[1736,2342,2343],{"class":1738,"line":1790},[1736,2344,2345],{},"Use `assets/readme-template.md` as the base structure.\n",[1736,2347,2348],{"class":1738,"line":1796},[1736,2349,2350],{},"Replace {{PLACEHOLDER}} markers with actual project data from the scan.\n",[1736,2352,2354],{"class":1738,"line":2353},12,[1736,2355,1747],{"emptyLinePlaceholder":790},[1736,2357,2359],{"class":1738,"line":2358},13,[1736,2360,2361],{},"### 4. Add badges\n",[1736,2363,2365],{"class":1738,"line":2364},14,[1736,2366,2367],{},"Read `assets/badges.json` for the full badge catalog.\n",[1736,2369,2371],{"class":1738,"line":2370},15,[1736,2372,2373],{},"Only include badges for things that actually exist.\n",[1736,2375,2377],{"class":1738,"line":2376},16,[1736,2378,1747],{"emptyLinePlaceholder":790},[1736,2380,2382],{"class":1738,"line":2381},17,[1736,2383,2384],{},"### 5. Validate the output\n",[1736,2386,2388],{"class":1738,"line":2387},18,[1736,2389,2390],{},"Review the generated README against the assertions in `evals/evals.json`.\n",[1736,2392,2394],{"class":1738,"line":2393},19,[1736,2395,1747],{"emptyLinePlaceholder":790},[1736,2397,2399],{"class":1738,"line":2398},20,[1736,2400,2401],{},"### 6. Optionally add a diagram\n",[1736,2403,2405],{"class":1738,"line":2404},21,[1736,2406,2407],{},"Only read `assets/diagrams.md` if the project has multiple components.\n",[11,2409,2410],{},"Short, focused, and easy to follow. Each step points to another file instead of trying to carry everything inline.",[23,2412,2414],{"id":2413},"the-script-handled-the-mechanical-work","The script handled the mechanical work",[11,2416,2417],{},"The biggest improvement was moving repo scanning into a script.",[11,2419,2420,2421,2424],{},"The skill now runs ",[179,2422,2423],{},"scripts/scan_project.sh \u003Cproject-directory>"," and gets structured JSON back with things like:",[70,2426,2427,2430,2433,2436,2439,2442,2445,2448],{},[73,2428,2429],{},"project name",[73,2431,2432],{},"description",[73,2434,2435],{},"license",[73,2437,2438],{},"owner and repo",[73,2440,2441],{},"package manager",[73,2443,2444],{},"CI provider and workflows",[73,2446,2447],{},"social links",[73,2449,2450],{},"directory structure",[11,2452,2453],{},"Instead of the agent improvising that detection work every time, it runs one script and gets clean, structured data back. Boring and repeatable. Exactly what you want for metadata gathering.",[11,2455,2456],{},"The current reference version also goes a bit further. It checks local files first, then uses the GitHub API to look up the repo homepage and crawls it for additional social links. That is a good example of how a skill can evolve — start with the reliable local-file path, then add enrichment once the core workflow is stable.",[23,2458,2460],{"id":2459},"references-and-assets-gave-everything-a-home","References and assets gave everything a home",[11,2462,2463],{},"The remaining pieces fell into two folders.",[11,2465,2466,2469],{},[179,2467,2468],{},"references/readme-best-practices.md"," holds the writing guidance: section order, tone, project-type adaptation, badge rules, and common pitfalls. The agent only reads it when it is about to write, not every time the skill loads.",[11,2471,2472,2475,2476,2479,2480,2483,2484,2487],{},[179,2473,2474],{},"assets/"," holds reusable inputs: ",[179,2477,2478],{},"badges.json"," for badge formats, ",[179,2481,2482],{},"readme-template.md"," for the base README structure, and ",[179,2485,2486],{},"diagrams.md"," for Mermaid templates when a project is complex enough to justify one.",[11,2489,2490],{},"This is where the skill becomes easy to customize. Want to change badge styles? Edit the badge catalog. Want a different README structure? Edit the template. Want to skip diagrams for simpler repos? The skill just avoids loading that asset entirely.",[11,2492,2493],{},"Keeping domain knowledge and data out of the main instructions makes the whole thing much easier to maintain.",[23,2495,2497],{"id":2496},"evals-made-the-quality-bar-explicit","Evals made the quality bar explicit",[11,2499,2500],{},"Once the skill was doing real work, I wanted a way to define what good actually meant.",[11,2502,2503],{},"That is what the evals are for.",[11,2505,2506,2507,2510],{},"The ",[179,2508,2509],{},"evals/evals.json"," file includes prompts for different cases:",[70,2512,2513,2516,2519,2522],{},[73,2514,2515],{},"a straightforward README improvement request",[73,2517,2518],{},"a casual \"make this look professional\" request",[73,2520,2521],{},"a minimal project that should not get bloated",[73,2523,2524],{},"a badge-focused request that should only generate real badges",[11,2526,2527],{},"I like this part because it forces the standards out into the open.",[11,2529,2530],{},"Instead of vaguely feeling that the README is better, you can check for specific things:",[70,2532,2533,2536,2539,2542,2545],{},[73,2534,2535],{},"no placeholder text",[73,2537,2538],{},"badges only for real metadata",[73,2540,2541],{},"Quick Start commands that match the detected package manager",[73,2543,2544],{},"section depth proportional to the project",[73,2546,2547],{},"no fabricated social links",[11,2549,2550],{},"That makes the skill easier to improve without drifting.",[23,2552,2554],{"id":2553},"the-larger-lesson","The larger lesson",[11,2556,2557],{},"The interesting thing about this project is not really README generation.",[11,2559,2560],{},"The larger lesson is that a useful skill usually stops looking like a prompt pretty quickly.",[11,2562,2563],{},"It becomes a small system.",[11,2565,2566],{},"Some parts should stay flexible and language-driven.",[11,2568,2569],{},"Some parts should be deterministic.",[11,2571,2572],{},"Some parts should be reusable data.",[11,2574,2575],{},"Some parts should act as tests.",[11,2577,2578],{},"Once you see that pattern, it applies to a lot more than READMEs:",[70,2580,2581,2584,2587,2590,2593,2596],{},[73,2582,2583],{},"commit message workflows",[73,2585,2586],{},"code review checklists",[73,2588,2589],{},"release note generation",[73,2591,2592],{},"internal documentation standards",[73,2594,2595],{},"repo audits",[73,2597,2598],{},"team-specific engineering conventions",[11,2600,2601],{},"That is the shift I find most useful when working with agents.",[11,2603,2604],{},"You stop asking the model to improvise the whole workflow every time.",[11,2606,2607],{},"Instead, you give it a structure that makes good behavior easier.",[23,2609,2611],{"id":2610},"if-you-want-to-build-your-own-skill","If you want to build your own skill",[11,2613,2614],{},"If you want to build your own skill, this is the path I would recommend:",[2260,2616,2617,2622,2625,2628,2631,2634,2637],{},[73,2618,2619,2620,891],{},"Start with one ",[179,2621,2140],{},[73,2623,2624],{},"Test it on a real project as early as possible.",[73,2626,2627],{},"Watch for repeated logic and consistency failures.",[73,2629,2630],{},"Move mechanical work into scripts.",[73,2632,2633],{},"Move domain knowledge into references.",[73,2635,2636],{},"Move templates and data into assets.",[73,2638,2639],{},"Add evals once the skill matters enough to maintain.",[11,2641,2642],{},"That sequence keeps the architecture earned.",[11,2644,2645],{},"You are not building a folder structure for its own sake. You are extracting parts only when they prove they deserve to exist.",[23,2647,2649],{"id":2648},"try-it-yourself","Try it yourself",[11,2651,2652],{},"If you want to explore the full tutorial series or inspect the finished reference implementation, the repo is here:",[11,2654,2655],{},[15,2656,2659],{"href":2657,"rel":2658},"https://github.com/debs-obrien/learn-agent-skills",[19],"debs-obrien/learn-agent-skills",[11,2661,2662],{},"And if you just want to try the skill without building it yourself (just make sure you chosse the readme-wizard although feel free to also install the good-morning one if you wish):",[299,2664,2668],{"className":2665,"code":2666,"language":2667,"meta":307,"style":307},"language-bash shiki shiki-themes github-light github-dark","npx skills add debs-obrien/learn-agent-skills\n","bash",[179,2669,2670],{"__ignoreMap":307},[1736,2671,2672,2676,2679,2682],{"class":1738,"line":1739},[1736,2673,2675],{"class":2674},"sScJk","npx",[1736,2677,2678],{"class":1935}," skills",[1736,2680,2681],{"class":1935}," add",[1736,2683,2684],{"class":1935}," debs-obrien/learn-agent-skills\n",[11,2686,2687],{},"Then open any project and tell your agent:",[299,2689,2692],{"className":2690,"code":2691,"language":304},[302],"Improve the README for this project using the readme-wizard skill.\n",[179,2693,2691],{"__ignoreMap":307},[11,2695,2696],{},"The point is not just that a skill can write a better README.",[11,2698,2699],{},"The point is how you get from a useful first draft to something reusable.",[2011,2701,2702],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":2704},[2705,2706,2707,2708,2709,2711,2712,2713,2714,2715,2716],{"id":2087,"depth":748,"text":2088},{"id":2126,"depth":748,"text":2127},{"id":2176,"depth":748,"text":2177},{"id":2227,"depth":748,"text":2228},{"id":2246,"depth":748,"text":2710},"SKILL.md became the orchestrator",{"id":2413,"depth":748,"text":2414},{"id":2459,"depth":748,"text":2460},{"id":2496,"depth":748,"text":2497},{"id":2553,"depth":748,"text":2554},{"id":2610,"depth":748,"text":2611},{"id":2648,"depth":748,"text":2649},"2026-03-24","How I turned a single-file agent skill into a maintainable README workflow with scripts, references, and evals.",{},"/blog/built-agent-skill-readme-wizard",{"title":2055,"description":2718},"blog/built-agent-skill-readme-wizard",[795,2724,796],"githubcopilot","61Rvu8Kb9vW0O8_edsu3f1FyuVOEjomqPvhnYDc08Lc",{"id":2727,"title":2728,"body":2729,"canonical":2848,"date":2849,"description":2850,"extension":786,"featured":787,"image":788,"meta":2851,"navigation":790,"ogimage":788,"path":2852,"provider":788,"published":790,"seo":2853,"stem":2854,"tags":2855,"url":788,"__hash__":2856},"blog/blog/build-websites-games-and-teaching-resources-with-google-gemini.md","Build Websites, Games, and Teaching Resources With Google Gemini for Free (No Coding Required)",{"type":8,"value":2730,"toc":2839},[2731,2734,2737,2741,2750,2757,2760,2764,2767,2770,2773,2777,2780,2783,2787,2790,2793,2796,2800,2803,2806,2810,2813,2816,2819,2823,2826,2833,2836],[11,2732,2733],{},"I literally came home from a podcast interview and my husband said, \"Debbie, I've built three websites.\" I said, \"What?\" And he said, \"Yes, I've built three websites.\" And I said, \"I heard you, but what?\"",[11,2735,2736],{},"My husband is not in tech. He has never cared about anything I do in tech. He does not know how to build anything. He works in the public sector and is rarely on the computer. And yet, he was able to build three websites in 10 minutes. He watched a video for 10 minutes and just went for it. I knew I had to tell the world about this.",[23,2738,2740],{"id":2739},"how-he-did-it","How he did it",[11,2742,2743,2744,2749],{},"He went to ",[15,2745,2748],{"href":2746,"rel":2747},"https://gemini.google.com",[19],"gemini.google.com",". That's it. This is free. He paid absolutely zero money. I do have a pro account, but he has a free account and can just build websites. Seriously, you've got to check it out.",[11,2751,2752,2753,2756],{},"When you open Gemini, you'll see a bunch of options like create image, create music, create video, write anything, help me learn. There's a lot you can play around with. But there are a couple of other things that I find are a little bit hidden. If you click into the options, you'll see ",[58,2754,2755],{},"Canvas",", deep research, guided learning, and more.",[11,2758,2759],{},"Canvas is the one we want. Open a canvas, pick the fast model (totally fine for this), and you're ready to go.",[23,2761,2763],{"id":2762},"building-a-batman-game-for-toddlers","Building a Batman game for toddlers",[11,2765,2766],{},"After my husband built his three websites, the next day he started building games for our kids. I wanted to show you what that looks like so I typed in \"build a Batman game for toddlers.\" That's it. A simple prompt.",[11,2768,2769],{},"The first thing that pops up is all this code. Don't be scared by that. As my husband said, \"I watched it spit out all the code that you normally write by hand.\" I normally write this code by hand. That's the insane thing.",[11,2771,2772],{},"Once the code finishes generating, it jumps into preview mode. And there it was. A Batman city helper game where you move Batman to collect stars. I was just moving with the trackpad and it worked. It was actually really simple for toddlers to play.",[23,2774,2776],{"id":2775},"just-use-your-imagination","Just use your imagination",[11,2778,2779],{},"The cool thing is you just have to have an imagination and think about how to make it better. I typed \"can you add sound?\" and Gemini added a cheerful ping whenever Batman catches a star or a balloon. I could hear the little sound effects and honestly it was amazing.",[11,2781,2782],{},"You can even select a specific area of the preview, drag a box over it, and ask Gemini to make changes right there. I dragged over the top area and said \"can you add the person's name here?\" You don't even have to specify \"the right hand corner\" or anything. Just drag and ask. It figured it out.",[23,2784,2786],{"id":2785},"building-multiple-things-at-the-same-time","Building multiple things at the same time",[11,2788,2789],{},"Here's something people don't even know about. While one thing is generating, you can open a new canvas and start building something else. I had the Batman game going and at the same time asked it to create a website for a circus act.",[11,2791,2792],{},"One line. That's all I typed. And out came a full circus website. \"A night of pure magic.\" It looked incredible. And I could iterate over it, add the actual address, YouTube links, whatever I wanted.",[11,2794,2795],{},"Then I created some games for learning numbers for toddlers. I used to be a school teacher. I used to have to prep and create all this stuff in my free time. And now I can just create resources on the fly.",[23,2797,2799],{"id":2798},"the-number-learning-game-blew-my-mind","The number learning game blew my mind",[11,2801,2802],{},"The toddler number game it created was called \"Number Fun\" with a bubble pop game. It actually spoke out loud and said \"pop the bubbles in order, start with one.\" You go one, two, three. I tried doing it wrong on purpose and it said \"find number four.\" It had a home button where you could click on count items and count fruits. One, two, three, four. It had sound you could turn on and off.",[11,2804,2805],{},"This is what kids want. This is the kind of interactive learning material that used to take ages to build. And I made it with one prompt.",[23,2807,2809],{"id":2808},"sharing-your-creations","Sharing your creations",[11,2811,2812],{},"You've got access to the code if you know how to code and want to tweak things yourself. But the really cool thing is the share button. You can copy the content, share it with someone, or just copy a link. My husband was sharing things with me saying \"here's a website I've started, can you help me improve it?\"",[11,2814,2815],{},"You can also go to previous versions and see changes saved. And there's an option to add Gemini features, which adds AI stuff to your creation. Great for writing stories. We actually created a book that reads the story to you. I can't even remember all the things we created in just a couple of minutes.",[11,2817,2818],{},"The one thing that is missing is there's no quick and easy deploy button. You can share a link and people can see it, which is kind of like it's deployed but not really deployed. It's a bit weird. But for getting a design together and having someone help you with the deployment part later? It works.",[23,2820,2822],{"id":2821},"the-only-limit-is-your-imagination","The only limit is your imagination",[11,2824,2825],{},"Whether you're creating games, building your own book, making material for teaching, or putting together a website and having someone help you with the deploy part, you're in control.",[11,2827,2828,2829,2832],{},"This is free. You might hit some limits if you keep going nonstop, and then you can pay for more. But to try it out, it costs nothing. Just go to ",[15,2830,2748],{"href":2746,"rel":2831},[19]," and start building.",[11,2834,2835],{},"I encourage you all to play around with this. Think about the possibilities. The technical barrier is gone. The only problem now is your imagination. So start being creative and just start imagining things.",[11,2837,2838],{},"Have fun building.",{"title":307,"searchDepth":748,"depth":748,"links":2840},[2841,2842,2843,2844,2845,2846,2847],{"id":2739,"depth":748,"text":2740},{"id":2762,"depth":748,"text":2763},{"id":2775,"depth":748,"text":2776},{"id":2785,"depth":748,"text":2786},{"id":2798,"depth":748,"text":2799},{"id":2808,"depth":748,"text":2809},{"id":2821,"depth":748,"text":2822},"https://dev.to/debs_obrien/build-websites-games-and-teaching-resources-with-google-gemini-for-free-no-coding-required-3gld","2026-03-15","Discover how Google Gemini's Canvas lets anyone build websites, games, and interactive teaching resources for free — no coding skills required.",{},"/blog/build-websites-games-and-teaching-resources-with-google-gemini",{"title":2728,"description":2850},"blog/build-websites-games-and-teaching-resources-with-google-gemini",[795,796],"WdUwTtiUhsdBOowORQmRR4SfdROJCjftDYAuJc_eI2U",{"id":2858,"title":2066,"body":2859,"canonical":3239,"date":3240,"description":3241,"extension":786,"featured":787,"image":788,"meta":3242,"navigation":790,"ogimage":788,"path":2065,"provider":788,"published":790,"seo":3243,"stem":3244,"tags":3245,"url":788,"__hash__":3246},"blog/blog/what-are-agent-skills-beginners-guide.md",{"type":8,"value":2860,"toc":3231},[2861,2864,2867,2871,2874,2888,2901,2905,2908,2912,2919,2924,2948,2954,2959,2968,2974,2979,2985,2991,2994,3014,3019,3026,3029,3035,3038,3042,3049,3054,3060,3069,3078,3084,3087,3091,3094,3098,3104,3110,3116,3119,3123,3126,3140,3143,3148,3154,3159,3165,3181,3185,3194,3197,3203,3210,3213,3219,3222,3228],[11,2862,2863],{},"AI agents are smart. But they're generic. Your agent is trained on a ton of general knowledge, but it doesn't have your specific domain knowledge. It doesn't know your preferences, your team's conventions, or how you personally want things done.",[11,2865,2866],{},"When we learn a new skill — playing basketball, riding a bike — we're adding knowledge we didn't have before. Skills work the same way for your agent. You give it the domain knowledge it's missing, personalized to how you want things done.",[138,2868,2870],{"id":2869},"what-is-a-skill","What is a skill?",[11,2872,2873],{},"A skill is a reusable set of instructions that teaches an AI agent how to do a specific task well. Think of it like a recipe card you hand to a talented chef. The chef knows how to cook, but they don't know your family's secret sauce. The recipe card tells them exactly what to do.",[70,2875,2876,2882],{},[73,2877,2878,2881],{},[58,2879,2880],{},"Without a skill"," → the agent produces generic output",[73,2883,2884,2887],{},[58,2885,2886],{},"With a skill"," → the agent follows your instructions and produces exactly what you want, every time",[11,2889,2890,2891,2894,2895,2897,2898,2900],{},"At its simplest, a skill is just ",[58,2892,2893],{},"one file",": a ",[179,2896,2140],{}," with a name, description, and instructions. That's it. You can add extras like scripts, references, assets, and evals — but you don't have to. All you need right now is the ",[179,2899,2140],{}," file.",[121,2902],{"src":2903,"alt":2904},"https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ys1th2hfjfp7d29oam26.png","anatomy of a skill",[11,2906,2907],{},"Let's build one.",[138,2909,2911],{"id":2910},"build-your-first-skill","Build your first skill",[11,2913,2914,2915,2918],{},"Open VS Code in your project directory. We're going to create a ",[179,2916,2917],{},"good-morning"," skill step by step.",[11,2920,2921],{},[58,2922,2923],{},"Step 1: Create the folder structure",[11,2925,2926,2927,829,2930,2933,2934,2937,2938,2941,2942,2944,2945,2947],{},"Create a new folder in your project root. You can use ",[179,2928,2929],{},".agents/",[179,2931,2932],{},".github/",", or ",[179,2935,2936],{},".claude/"," — they all work. The ",[179,2939,2940],{},".agents/skills/"," path is the cross-agent convention that works with Copilot. Inside that, create a ",[179,2943,60],{}," folder, and inside that, create a folder called ",[179,2946,2917],{},". This folder name is your skill's name.",[299,2949,2952],{"className":2950,"code":2951,"language":304},[302],"your-project/\n└── .agents/\n    └── skills/\n        └── good-morning/\n",[179,2953,2951],{"__ignoreMap":307},[11,2955,2956],{},[58,2957,2958],{},"Step 2: Create the SKILL.md file",[11,2960,2961,2962,2964,2965,2967],{},"Inside the ",[179,2963,2917],{}," folder, create a file called ",[179,2966,2140],{},". It must be in capital letters — that's how agents find it.",[299,2969,2972],{"className":2970,"code":2971,"language":304},[302],"your-project/\n└── .agents/\n    └── skills/\n        └── good-morning/\n            └── SKILL.md\n",[179,2973,2971],{"__ignoreMap":307},[11,2975,2976],{},[58,2977,2978],{},"Step 3: Add the frontmatter",[11,2980,2981,2982,2984],{},"Open ",[179,2983,2140],{}," and add the YAML frontmatter at the top:",[299,2986,2989],{"className":2987,"code":2988,"language":304},[302],"---\nname: good-morning\ndescription: \"A skill that responds to good morning with a cheerful greeting\"\n---\n",[179,2990,2988],{"__ignoreMap":307},[11,2992,2993],{},"Two important things here:",[70,2995,2996,3008],{},[73,2997,2998,3001,3002,3004,3005,3007],{},[58,2999,3000],{},"The name must match the folder name."," If the folder is called ",[179,3003,2917],{},", the name must be ",[179,3006,2917],{},". If they don't match, your editor will flag it.",[73,3009,3010,3013],{},[58,3011,3012],{},"The name and description are always in context."," Every time you're working in this project, the agent sees the name and description so it knows what skills are available. Keep the description short and specific, this is how the agent knows when to use the skill.",[11,3015,3016],{},[58,3017,3018],{},"Step 4: Write the instructions",[11,3020,3021,3022,3025],{},"Everything below the frontmatter is the skill body. This only gets added to context ",[58,3023,3024],{},"when the skill is called",", not all the time. The agent only loads these instructions when it decides to use the skill.",[11,3027,3028],{},"Add the body below the frontmatter:",[299,3030,3033],{"className":3031,"code":3032,"language":304},[302],"---\nname: good-morning\ndescription: A skill that responds to good morning with a cheerful greeting\n---\n\n# Good Morning Skill\n\nWhen the user says good morning, respond with:\n- \"Hi Debbie, hope you have a great day!\"\n- Ask if they have done any sport today\n- Include a funny joke about sports\n\n## Example\n\n**User:** Good morning\n**Agent:** Hi Debbie, have you done any sport today? Here's a funny joke about sports: Why did the soccer player bring string to the game? Because he wanted to tie the score!\n",[179,3034,3032],{"__ignoreMap":307},[11,3036,3037],{},"That's the complete skill. One file. A few lines of instructions. Make it as personal as you like, put your own name in there, change the topic from sports to whatever you want.",[138,3039,3041],{"id":3040},"test-it","Test it",[11,3043,3044,3045,3048],{},"Start a ",[58,3046,3047],{},"new session"," from the same directory (skills are discovered at session start) and type:",[1713,3050,3051],{},[11,3052,3053],{},"Good morning",[11,3055,3056,3057,3059],{},"The agent finds the skill, reads the ",[179,3058,2140],{}," file, and responds.",[11,3061,3062,3065,3066],{},[58,3063,3064],{},"In GitHub Copilot",": ",[133,3067,3068],{},"\"Hi Debbie, have you done any sport today? Here's a funny joke about sports: Why did the bicycle fall over? Because it was too tired from all that cycling!\"",[11,3070,3071,3074,3075],{},[58,3072,3073],{},"In Claude Code",": Open Claude Code from the same project directory, say \"good morning\", and you get the same thing: ",[133,3076,3077],{},"\"Hi Debbie, have you done any sport today? Here's a funny joke for you: Why do basketball players love donuts? Because they can always dunk them!\"",[11,3079,3080,3081,3083],{},"Skills work across agents. The same ",[179,3082,2140],{}," file works in Copilot, Claude Code, and others. Each agent discovers the skill, reads the instructions, and follows them.",[11,3085,3086],{},"That's a skill in action. Now imagine instead of \"good morning\", the instructions told the agent how to generate a polished README, write commit messages in your team's format, or review code against your standards. Same idea, bigger impact.",[138,3088,3090],{"id":3089},"how-skills-get-loaded","How skills get loaded",[11,3092,3093],{},"Skills are designed to be efficient with context windows. They use a three-level loading system. The agent only loads what it needs, when it needs it.",[121,3095],{"src":3096,"alt":3097},"https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a1kun34lhdwapm8s56py.png","how skills get loaded",[11,3099,3100,3103],{},[58,3101,3102],{},"Level 1"," is always in the agent's context. It's just the name and description (~100 words). This is how the agent decides whether to use the skill. If someone says \"improve my README\", the agent scans its available skills and picks the one whose description matches.",[11,3105,3106,3109],{},[58,3107,3108],{},"Level 2"," loads when the skill triggers. The full SKILL.md body with all the instructions, steps, and examples. This is ideally under 500 lines.",[11,3111,3112,3115],{},[58,3113,3114],{},"Level 3"," loads on demand. Scripts, references, and assets that the agent pulls in only when it needs them. Scripts can even run without being loaded into context at all, saving tokens. And some resources might not load at all for certain projects. For example, a diagram template file only needs to be read if the project is complex enough to need an architecture diagram. Simple projects skip it entirely.",[11,3117,3118],{},"This matters because context windows are limited. A well-designed skill is lean at the top and detailed at the bottom.",[138,3120,3122],{"id":3121},"where-skills-live","Where skills live",[11,3124,3125],{},"Skills can be installed at two levels:",[70,3127,3128,3134],{},[73,3129,3130,3133],{},[58,3131,3132],{},"Project-level",": in your project directory, available only when you're in that directory",[73,3135,3136,3139],{},[58,3137,3138],{},"Global",": in your home directory, available from anywhere",[11,3141,3142],{},"Each agent checks slightly different locations:",[11,3144,3145,1087],{},[58,3146,3147],{},"GitHub Copilot (VS Code)",[299,3149,3152],{"className":3150,"code":3151,"language":304},[302],"# Project-level (any of these work)\nyour-project/.github/skills/\nyour-project/.claude/skills/\nyour-project/.agents/skills/\n\n# Personal (works from any directory)\n~/.copilot/skills/\n~/.claude/skills/\n~/.agents/skills/\n",[179,3153,3151],{"__ignoreMap":307},[11,3155,3156,1087],{},[58,3157,3158],{},"Claude Code",[299,3160,3163],{"className":3161,"code":3162,"language":304},[302],"# Project-level\nyour-project/.claude/skills/\n\n# Personal (works from any directory)\n~/.claude/skills/\n",[179,3164,3162],{"__ignoreMap":307},[11,3166,2506,3167,3169,3170,3175,3176,3178,3179,891],{},[179,3168,2940],{}," path is part of the ",[15,3171,3174],{"href":3172,"rel":3173},"https://agentskills.io",[19],"Agent Skills open standard"," which is a cross-tool standard, but Claude Code uses its own ",[179,3177,2936],{}," directory structure, not ",[179,3180,2929],{},[138,3182,3184],{"id":3183},"the-skills-ecosystem","The skills ecosystem",[11,3186,3187,3188,3193],{},"There's a whole directory of skills at ",[15,3189,3192],{"href":3190,"rel":3191},"https://skills.sh",[19],"skills.sh"," where you can browse and discover skills built by the community.",[11,3195,3196],{},"To install a skill, use the skills CLI:",[299,3198,3201],{"className":3199,"code":3200,"language":304},[302],"npx skills add anthropics/skills --skill skill-creator\n",[179,3202,3200],{"__ignoreMap":307},[11,3204,3205,3206,3209],{},"This installs the ",[179,3207,3208],{},"skill-creator"," skill from Anthropic. A skill that helps you create other skills. One command and it's ready to use.",[11,3211,3212],{},"You can see what you have installed:",[299,3214,3217],{"className":3215,"code":3216,"language":304},[302],"npx skills list\n",[179,3218,3216],{"__ignoreMap":307},[11,3220,3221],{},"And search for skills:",[299,3223,3226],{"className":3224,"code":3225,"language":304},[302],"npx skills find\n",[179,3227,3225],{"__ignoreMap":307},[11,3229,3230],{},"Skills work across multiple AI agents — Copilot, Claude Code, Cursor, Goose, and many more. The skills CLI handles installing to the right location for each agent.",{"title":307,"searchDepth":748,"depth":748,"links":3232},[3233,3234,3235,3236,3237,3238],{"id":2869,"depth":756,"text":2870},{"id":2910,"depth":756,"text":2911},{"id":3040,"depth":756,"text":3041},{"id":3089,"depth":756,"text":3090},{"id":3121,"depth":756,"text":3122},{"id":3183,"depth":756,"text":3184},"https://dev.to/debs_obrien/what-are-agent-skills-beginners-guide-e2n","2026-03-04","A beginner's guide to AI agent skills — what they are, how they work, and how to build your first skill in minutes.",{},{"title":2066,"description":3241},"blog/what-are-agent-skills-beginners-guide",[795,796],"FXbInxOO8NDLoT0Iy3DEsxvutB50aCannhWPdji6D54",{"id":3248,"title":3249,"body":3250,"canonical":3313,"date":3314,"description":3315,"extension":786,"featured":787,"image":788,"meta":3316,"navigation":790,"ogimage":788,"path":3317,"provider":788,"published":790,"seo":3318,"stem":3319,"tags":3320,"url":788,"__hash__":3322},"blog/blog/ai-agents-mcp-automate-content.md","How I Use AI Agents + MCP to Fully Automate My Website's Content",{"type":8,"value":3251,"toc":3307},[3252,3255,3259,3262,3266,3269,3272,3276,3279,3282,3285,3288,3291,3295,3298,3301,3304],[11,3253,3254],{},"Recently I have been playing with a lot of tools to help automate simple tasks just so I can keep my website up to date. As I create a lot of content from videos to blog posts and appear as guests on many podcasts I want to have this reflected on my site as it's good to have all this info in one place to easily share with others and it's also great to look back on. But it is tedious and it takes time, time that I have very little of. So this is a perfect use case for AI to take over this task. So where do I start?",[23,3256,3258],{"id":3257},"before-ai","Before AI",[11,3260,3261],{},"First of all let me tell you what it is like to add a new podcast episode to my site. I use Nuxt content so basically each podcast is just a markdown file with some yaml. This yaml contains things like the date, the name of the podcast the image url and the host for example. Last year I was simply getting an old podcast episode and clicking duplicate in VSCode and then renaming everything with the new podcast information. So basically manually clicking the link to new episode and copying and pasting the information from that site where it hosted into my markdown file. Then I had to download the image and upload it to cloudinary, get the image name from there and paste that into my file. Cloudinary is great for managing images and keeping my site performant but the extra work of downloading and uploading the images was tedious and meant that sometimes it took me ages to add new episodes to my site cause I simply couldn't be bothered doing it.",[23,3263,3265],{"id":3264},"automating-with-prompts-and-the-playwright-mcp","Automating with Prompts and the Playwright MCP",[11,3267,3268],{},"I started to automate some of the process by using reusable prompts in VSCode. I created instructions for Copilot of what it needed to do and then all I had to do was press the play button to run the prompt in a new chat and then give it the URL for the podcast. I had the Playwright MCP installed so Copilot would use it to navigate to the URL of where the podcast was hosted and find the relevant information it needed to complete the metadata. It was pretty good and saved a lot of time and I could even bulk update new episodes by giving it more than one URL.",[11,3270,3271],{},"However the images were still an issue and I was tempted to actually just stop using Cloudinary just cause it was quicker and easier to use images stored in the public folder of my site. But then I would lose out on the benefits of Cloudinary and its image optimization.",[23,3273,3275],{"id":3274},"automating-with-goose-playwright-mcp-cloudinary-mcp-github-mcp","Automating with Goose, Playwright MCP, Cloudinary MCP & GitHub MCP",[11,3277,3278],{},"I then started playing around with Goose, a coding agent from Block. Goose is a desktop app although there is also a CLI available. I decided to give it a go and see if I could improve the way I automated this process. I probably could have continued playing around in VSCode and achieved similar but lately I have been trying to code, or should I say, get tasks done, without using an editor. As in just review the pull request later on CI and let the agent do it's thing cause I really believe this is the way we are heading so I want to keep experimenting on how it feels to code this way.",[11,3280,3281],{},"So I copied my prompt into Goose and saved it as a recipe. Recipes seem pretty similar to prompts but you can use parameters so I could add the podcast url as a parameter and it will automatically get detected. There are also a lot of other options which I haven't got round to properly checking out but this seemed enough for my use case. Now I have the Playwright MCP navigating to the site and getting all the info I need for the podcast page. I then just asked Goose to download the image for me and add it locally and it did. This was great but did I really want to just stop using Cloudinary just cause I was lazy!",[11,3283,3284],{},"So I thought what if Cloudinary had an MCP and then Goose could just use that MCP server to upload the image and then update the image metadata with the correct image id. Now if it could do that then all my problems would be solved. And so I looked in Goose's extensions and searched for Cloudinary and would you believe it there was an MCP server for Cloudinary. Not only that but it actually worked. Goose used the Playwright MCP to navigate to the site and get all the content it needed including the image and then the Cloudinary MCP was used to add the image to my Cloudinary account using my API key stored in the extension's settings. It even figured out which folder to save it to without me asking.",[11,3286,3287],{},"And that was it. It all just worked. I checked my Cloudinary account and the images were there. I then asked Goose to run the dev server and verify its work using the Playwright MCP by navigating to the podcasts page to ensure everything looked as it should. Not only could I see the browser being opened and see the new podcast episodes with images but I could also ask for a screenshot of the page.",[11,3289,3290],{},"Then one more thing of course. We had come so far so may as well finish it all off. I then asked Goose to create a pull request which it did using the GitHub MCP which I previously configured. I then reviewed the code just in case anything looked wrong, especially with regards to the cloudinary URL, even though I had visually reviewed it and as you can imagine, it was good to go. I merged it and new podcast episodes were added to my site.",[23,3292,3294],{"id":3293},"conclusion","Conclusion",[11,3296,3297],{},"So here's the thing. It took me time to figure all this out and set up the process and ensure it was all working. Yes it would have been quicker to copy and paste myself. But now it's done and the next time I want to add a podcast episode I just have to run my recipe in Goose and pass in the podcast URL. I am a guest on one tonight so when that is out I will be able to add it easily to the site. In fact if I had a team of people working on my site I could even share the recipe with them and they could simply run it.",[11,3299,3300],{},"I am using Nuxt content for my site which means I have no CMS. My content lives in markdown files and it makes it very easy as a developer to add content but perhaps not so easy for non developers. But now, now even my mother could add a new podcast episode to my site. That is just amazing. This is just my personal site but think about the possibilities of this use case for many other businesses.",[11,3302,3303],{},"I am very impressed with what Goose can do. The more I am using it the more it is blowing my mind. I am now going to go ahead and add other recipes for the rest of the content I add or perhaps just modify this recipe with parameters so I can have one recipe. I shall keep playing around. This is fun.",[11,3305,3306],{},"Let me know if you found this interesting or are doing something similar or have used any of the MCPs mentioned above. We are living in exciting times so if you haven't started to experiment yet then what are you waiting for. Just play around and have fun.",{"title":307,"searchDepth":748,"depth":748,"links":3308},[3309,3310,3311,3312],{"id":3257,"depth":748,"text":3258},{"id":3264,"depth":748,"text":3265},{"id":3274,"depth":748,"text":3275},{"id":3293,"depth":748,"text":3294},"https://dev.to/debs_obrien/how-i-use-ai-agents-mcp-to-fully-automate-my-websites-content-3ekj","2026-01-14","How I use AI agents and MCP tools to automate publishing and updating podcasts, videos, and other content on my website.",{},"/blog/ai-agents-mcp-automate-content",{"title":3249,"description":3315},"blog/ai-agents-mcp-automate-content",[3321,795],"mcp","l5miSIyIreTbBGlxurdtwDT388emy_NZqto7IYUAd5Q",{"id":3324,"title":3325,"body":3326,"canonical":788,"date":3454,"description":3330,"extension":786,"featured":787,"image":3455,"meta":3456,"navigation":790,"ogimage":788,"path":3459,"provider":3460,"published":790,"seo":3461,"stem":3462,"tags":3463,"url":788,"__hash__":3465},"blog/blog/first-two-years-raising-kids-working-fulltime.md","First Two Years of Raising Kids and Working Fulltime",{"type":8,"value":3327,"toc":3441},[3328,3331,3335,3338,3342,3345,3348,3352,3355,3359,3362,3365,3369,3372,3375,3378,3381,3384,3387,3390,3393,3397,3400,3403,3406,3410,3413,3416,3420,3423,3426,3430,3433,3435,3438],[11,3329,3330],{},"I now have two year old twins yet I also work fulltime and manage to raise my kids with no daycare or family help. Many people ask me how I do it and sometimes I even wonder myself! In this post, I want to share my experiences, challenges, and strategies for balancing work and parenthood over the past two years.",[23,3332,3334],{"id":3333},"before-kids","Before Kids",[11,3336,3337],{},"Before having kids my life was very different. I was doing 3 hours of sport a day. I had so much free time and I enjoyed every minute of it. I might work in the morning or go for a long cycle. Balancing things was really easy. And I traveled a lot for work attending many conferences and sometimes doing some back to back which meant traveling for 3 weeks at a time. I had a lot of time for community projects too and did a lot of mentoring. I love giving back to the community and helping others in any way I can. I never consider my job as stressful because I love what I do and sometimes I wonder how I am getting paid to do something I love so much. Don't get me wrong, there are times when there is stress and deadlines but to be honest that just pushes me to do better and I thrive under pressure.",[23,3339,3341],{"id":3340},"the-transition-becoming-a-parent","The Transition: Becoming a Parent",[11,3343,3344],{},"I had been trying to have kids for 13 years and had gone through multiple failed rounds of fertility treatment. I had given up and focused on my career and then years later I decided to give it another shot and this time on the second round I got pregnant. Just after getting the news I flew to Romania for a conference and then to Seattle for Microsoft Build. It was still really early days and I was so worried about people finding out so I did my best to go about my day as normal as I could. I went for runs with my colleagues and friends but really monitored my heart rate and made sure I didn't push myself too much. I was so hungry but luckily there were plenty of snacks around so it just meant I was always eating. And I avoided alcohol which people didn't question too much as we were all working so hard anyway and being tired was normal as I had so many talks and things to do it was non stop so I avoided staying out late.",[11,3346,3347],{},"When I got back from Seattle is when I went for the scan and we found out it was twins and that is when I was told to slow down and not do any more running. This was a high risk pregnancy and cause of my age and twins I had to be careful so I found a gym for pregnant women ran by midwives and did pregnancy yoga and cross fit. I trained almost every day and went for many walks in the mountains which doctors told me to continue doing as my blood pressure was so good and no swollen ankles or any problems at all. I was super fit and healthy and kept as active as possible. I continued to work until 3 days before they were born when tiredness kicked in so much that I felt I wasn't giving it my all and I needed to take it easy so I did.",[138,3349,3351],{"id":3350},"first-months-home","First Months Home",[11,3353,3354],{},"Having twins was hard. They came early at 34 weeks and we had to spend two weeks in the hospital which is another long story in itself. And once we got home as many parent knows it really is sleepless nights and constant feeding and changing nappies. I was so exhausted but luckily I had my husband home for the first 2 months so we just managed to get through it. I had been told that the first two months were the hardest so I knew if I got to that milestone then I would be able to handle the rest.",[138,3356,3358],{"id":3357},"returning-to-work","Returning to Work",[11,3360,3361],{},"I had 17 weeks of maternity leave but I was keen to get back to work sooner than later. Most people told me to take my time and not rush back but I love my job and I get to work from home and get to choose my own hours so it really makes things so much easier. I decided that if I go back to work part time I could extend the maternity leave and not have to go back fulltime until October so that is what I did after 3 months off. That meant I only had to find 4 hours to work each day which was manageable. I would work when the babies were napping or in the evening after they went to bed. I keep a super strict schedule for the boys cause I have twins so its seriously necessary. That means their bedtime is at 7pm and they sleep till 7am and then there were naps. At first it was two naps a day and then it went down to one nap a day. They started sleeping through the night at about 4 months and yes the odd time there are wake ups but nothing major.",[11,3363,3364],{},"Going back to work was great and I have to say I was working with an amazing team who were super supportive. That meant if a baby woke up or was sick or I needed to feed at a different time than planned, it wasn't a problem. Sometimes I could just turn off my camera and feed the boys or sometimes I would just leave and catch up with the team later. This meant I could keep breastfeeding right up until they were 19 months old cause I had the support and flexibility to do so.",[23,3366,3368],{"id":3367},"back-to-fulltime-work","Back to Fulltime Work",[11,3370,3371],{},"I didn't find it difficult to do 4 hours a day but when the transition came to work 8 hours it was a bit more challenging. I had to be super organized and have everything planned out. I often describe my schedule like a military operation. Every minute is planned and scheduled. I have to be super efficient with my time and make sure I am not wasting any time at all. I have a strict routine for the boys so their sleeping schedule is always the same and they sleep 14 hours a day and still do.",[11,3373,3374],{},"So how do I find 8 hours a day to work? First of all I have to say that my husband is great and when I am working and they are awake he is with the boys and when he is with the boys that might include taking them to a playground or cooking dinner or doing bath and bedtime routine. We work as a great team and that is how it works so well. With his schedule it means he is home for about 3 hours a day when they are awake. That means I can work 3 hours when he is home. 2 hours when they nap, thats 5 hours and then I only have to work 3 more hours when they are asleep in the evening. They sleep at 7 and I normally work till 11pm. Thats actually 9 hours. which gives me an hour of flexibility, incase they nap a bit less or takes longer to get them to sleep or whatever. But see its easily doable. It's just maths.",[11,3376,3377],{},"Now what that means is that when I am with the boys everything else has to be done too. That means shopping, cooking, cleaning, laundry, gardening, playtime, etc. I will be honest and say that I do pay for a cleaner to come once a week to really clean the house properly as it is hard to find the time to do everything and nowadays she also spends an hour putting the washing away as its easy to take it out of the dryer but I do lack time in putting it in the wardrobes. Need to work on that.",[11,3379,3380],{},"Cooking with the boys is fun. I tend to cook meals which take no longer than 15 minutes or can be put in an oven. When they were smaller they were in a bouncer and when they were 13 months they were in learning towers. My cooking was like doing a youtube video and I would constantly show them what I was doing, let them feel the vegetables, crack eggs, mix and play with pots and pans or wooden veg that they chop in half. Sometimes I cook and the kitchen floor is like a mine field with plastic plates and spoons and pots everywhere. But its manageable.",[11,3382,3383],{},"I eat when the boys eat so they had to learn to feed themselves early which they did. And I eat the same as them or they eat the same as me so that makes things much easier as its just one meal I have to cook. They eat everything from curries, to stews, fish, meat, veg etc. It is super easy to cook for them. Sometimes my kitchen is a complete mess and I just leave it for my husband to do when he gets home. Other times they play nicely and I get a few minutes to clean the kitchen. The key is to not get stressed about it. It will all get done eventually. And I am pretty good at making sure that it gets done. I never go to bed with a messy kitchen or toys not put away. That would mess up things for the next day.",[11,3385,3386],{},"Shopping can be hard and I normally only buy what can fit in the buggy and if there is bulky stuff I do that online and get it delivered or we do it at the weekend when my husband is around but I love going to the supermarket with the boys and letting them roam the aisles look for things we need. I am constantly saying, where is your brother and have taught them to always make sure they are close to each other which makes it a bit easier.",[11,3388,3389],{},"My washing machine is in the garage so that was a bit of a challenge and sometimes meant me running downstairs to put washing on and then running back up and hoping they hadn't done anything they shouldn't or weren't fighting with each other. Hanging clothes on the washing line took too much time and wasn't possible so I just threw everything in the dryer which isn't ideal but it works.",[11,3391,3392],{},"The key to raising twins is to make sure they have different things to do and that you constantly change their toys and activities so they don't get bored. I didn't actually buy a lot of toys, but instead bought climbing frames, tunnels, ride on toys, books, musical instruments and toy hoovers and mops. They love helping with chores so if I give them a job to do then I have 15 minutes of being able to get something done such as the laundry or sweeping the leaves.",[23,3394,3396],{"id":3395},"finding-time-for-myself","Finding Time for Myself",[11,3398,3399],{},"Now you may ask where is the time for myself. Well it doesn't really exist. I sleep from 11pm till 7am and sleep great and in seconds. Sometimes I wake at 6 and do sport before they wake. Sometimes I do sport in my garage while they are napping and watch a meeting at the same time. I have often chopped vegetables while listening to a recorded meeting and anytime I can maximize my time I do. My showers are super short or are with the boys. Sometimes I also do some training while with them. I built them a climbing gym next to mine so they can climb and swing while I do some pull ups or weights or things I can do quickly and easily. It is not easy at all but I do not work weekends so at weekends I can go for a long run, or read a book or cook something that takes longer than 15 minutes.",[11,3401,3402],{},"To be honest I am used to working 2 jobs most my life and that included working weekends so having weekends free is a luxury for me. And if something were to happen during the week that I was not able to do my job then I always had the weekend to fall back on and catch up. Although I think I only ever had to do that once.",[11,3404,3405],{},"I don't watch Netflix or TV anymore. I don't have the time for it however if I do get some time I much prefer to watch some coding stuff on YouTube. It brings me more value as I learn something from it. There is too much gong on in my brain for me to sit down and watch a movie. I get bored and my mind wonders and thinks about all the other things I could be doing. I also don't spend a lot of time on social media. Your mobile in general can steal so much of your time. I do check social media a few times a day but I don't spend too much time on it and I limit myself to how many. For example I don't have instagram cause I don't want to spend time there. There are enough ways for people to follow me. Of course maybe there is a way to repost across these but I just avoid cause then its less people to keep up with too.",[23,3407,3409],{"id":3408},"traveling-with-kids","Traveling with Kids",[11,3411,3412],{},"I still travel a lot to conferences and I bring my kids and husband everytime. People told me at first that I won't be able to travel when I have twins and were all so negative. I think it made me more determined to make it work. The boys are two and I have lost count of the amount of flights they have been on but its over 30 at this stage. They have been to America twice, once for Microsoft Build and the other for a team week in San Francisco. On that trip we took a few weeks holidays and did a road trip from Los Angeles to Joshua Tree national park the to the Grand Canyon were we stayed in a cabin then on to Las Vegas, then Yosemite National Park and then to San Francisco to work for a week. The boys were 1 year old and therefore it was pretty easy to travel with them. It just meant lots of planning and being super organized but also going with the flow. We didn't have hotels booked and would plan to arrive to a town but if we didn't make it then we just stopped somewhere else and that worked really well for us.",[11,3414,3415],{},"When we travel to events where I have to work then my husband takes the boys to playgrounds, childrens museums or just parks while I am working. Sometimes I do get jealous that he gets to do such cool things with the boys while we are away but I also ensure that we do have some family time too so that I am not just working and we also get to see the sights together and have those memories as a family. I am very lucky in that my husband gets a lot of time off form work, much more than I do so that helps a lot. Yes we do have the extra expense of flights and food etc while away but it is so worth it as the boys get to see the world and experience new things. So I intend to keep traveling with them until it becomes too much hassle. But for now it works and we all have fun. Tiring for sure but life is tiring anyway with kids so why not travel, meet new people and have new experiences.",[23,3417,3419],{"id":3418},"unexpected-wins","Unexpected Wins",[11,3421,3422],{},"I was very worried about under performing when returning to work after maternity leave. Could I give it my all. Would I be able to have the energy to do everything I needed to do both in work and outside of work. Yet not only did I manage to do an amazing job but I also got promoted during this time which is wild when I think about it and just proves that you really are capable of anything when you give it your all.",[11,3424,3425],{},"The boys are now 2. Things are so much easier. They are potty trained so no more nappies. They are still sleeping great. They eat anything and love going to restaurants. We go out at least once a day on bikes or scooters and they love adventure. They now play together and entertain each other and therefore I have more time to get things done. I have a system that works and that I have learnt from mistakes and adapted as the boys grew cause everything changes and it constantly will. Being ready to embrace change and new challenges is key to success in everything we do. I am a multitasker and with twins I also have octopus arms and super fast eyes as have to watch two little people often running in different directions. Life is fun and busy and I wouldn't have it any other way.",[23,3427,3429],{"id":3428},"looking-ahead","Looking ahead",[11,3431,3432],{},"As this new year has just started along with it will bring new challenges and soon hopefully a new job, working with new people and learning new skills. I am very grateful for the fact that I can work remote and that I have worked with people who care and who allow me to put my family first. I think because of the weird working schedule I do, it allows me to do my best work because I never feel I am missing out on anything. I get to be there for my kids but I also get to work and do what I love. My office is right next to the playroom so even if I am in the office for a long block of time I can still see them or take a 5 minute break and say hi. This is so good for mental health. Also between my working hours when I am with the boys I completely switch off and I am surrounded by nature or by them and that recharges me so much. I think having that balance is so important. I don't think I would do as good a job if I was expected to sit in an office for 8 hours a day.",[23,3434,3294],{"id":3293},[11,3436,3437],{},"Life has been crazy busy since having kids but I managed to find a balance that works for me and my family. It requires careful planning, flexibility, and a supportive partner, but it is definitely possible to raise kids while working fulltime, if you have the right job and flexibility of hours. Remote work works for me but might not work for everyone. Plus I have a separate space where I can work and that helps a lot. For me the key is to stay organized, prioritize your time as much as possible, and embrace the chaos that comes with parenthood especially with twins. Looking back at the past two years, I am proud of what I have accomplished both as a parent and a professional, it has been exhausting, I won't lie, but it sure has been a lot of fun and I look forward to many more years of chaos and fun.",[11,3439,3440],{},"If you ever want to chat about parenthood and working fulltime feel free to reach out to me. If you are thinking of becoming a parent and are worried about juggling both, know that it is possible and totally worth it.",{"title":307,"searchDepth":748,"depth":748,"links":3442},[3443,3444,3448,3449,3450,3451,3452,3453],{"id":3333,"depth":748,"text":3334},{"id":3340,"depth":748,"text":3341,"children":3445},[3446,3447],{"id":3350,"depth":756,"text":3351},{"id":3357,"depth":756,"text":3358},{"id":3367,"depth":748,"text":3368},{"id":3395,"depth":748,"text":3396},{"id":3408,"depth":748,"text":3409},{"id":3418,"depth":748,"text":3419},{"id":3428,"depth":748,"text":3429},{"id":3293,"depth":748,"text":3294},"2026-01-11","v1640965793/debbie.codes/blog/2026/family-microsoft",{"ogImage":3457,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1640965793/debbie.codes/blog/2026/family-microsoft","eager","/blog/first-two-years-raising-kids-working-fulltime","cloudinary",{"title":3325,"description":3330},"blog/first-two-years-raising-kids-working-fulltime",[3464],"personal","3_WD7TzveyoXkG-TB_hYsnefySjNGc6Y2u6HS1SvrmY",[3467,3715,4379,4817,5223,5243,5289,5497,5515,5534,5550,5753,5989,6218,6253,6271,6352,8683,11424,13621,15419,17216,19858,20314,20477,20495,20905,20924,20942,20959,21136,21836,22330,22532,22778,23004,23743,23901,24381,24513,24666,24683,24772,24865,25078,25559,25844,26322,26339,26741,26803,26821,27694,27712,27933,28023,28466,28484,28878,29406,29997,30048,30338,31973,32982,32999,33017,36405,36711,36728,36802,37036,37287,37539,37760,37911,38182,38877,39158,39453,39933,40087,41029,41384,42295,42865,44134,45088,45475,45664,45956,47200,47518,47535,47552,47680,47871,47889,48318,48597,48615,49486,50874,51284,51581,52521,52537,53576,54650,54667,55855,55951,56277,56714,56979,56996,57162,57272,57568,57584],{"id":3468,"title":3469,"body":3470,"canonical":788,"date":3706,"description":3707,"extension":786,"featured":787,"image":3708,"meta":3709,"navigation":790,"ogimage":788,"path":3710,"provider":3460,"published":787,"seo":3711,"stem":3712,"tags":3713,"url":788,"__hash__":3714},"blog/blog/2020-in-review.md","2020 - my most successful year (career wise)",{"type":8,"value":3471,"toc":3689},[3472,3475,3479,3519,3523,3526,3529,3532,3535,3539,3542,3545,3549,3552,3555,3558,3562,3565,3568,3571,3574,3578,3581,3584,3587,3591,3594,3598,3601,3605,3608,3612,3615,3618,3622,3625,3634,3637,3640,3644,3647,3650,3653,3656,3660,3663,3666,3669,3673,3676,3679,3683,3686],[11,3473,3474],{},"We all know that 2020 has been a hard year especially family wise and it has of course been mentally challenging in more ways than one. We all know and will remember these hard times forever. However, I would like to focus on the positive side of things and recap the year looking only at that. If I do that then 2020 has been my most successful year ever. And I think it is important to look back and acknowledge these achievements as I feel they mean even more during 2020 than any other year.",[23,3476,3478],{"id":3477},"achievements","Achievements:",[70,3480,3481,3488,3495,3498,3501,3504,3507,3510,3513,3516],{},[73,3482,3483],{},[15,3484,3487],{"href":3485,"rel":3486},"https://mvp.microsoft.com/en-us/PublicProfile/5003613?fullName=Debbie%20O%27Brien",[19],"Microsoft MVP",[73,3489,3490],{},[15,3491,3494],{"href":3492,"rel":3493},"https://stars.github.com/profiles/debs-obrien/",[19],"Github Star",[73,3496,3497],{},"26 talks",[73,3499,3500],{},"10 blog posts",[73,3502,3503],{},"52 videos and podcasts",[73,3505,3506],{},"1 workshop",[73,3508,3509],{},"778 commits",[73,3511,3512],{},"192 pull requests",[73,3514,3515],{},"104 stars",[73,3517,3518],{},"24 issues",[23,3520,3522],{"id":3521},"where-it-all-began","Where it all began:",[11,3524,3525],{},"I started 2020 with an email from Microsoft saying I was now a Microsoft Most Valuable Professional in Developer technologies, this for me was a major goal and like a dream come true to be accepted into the MVP program. What a way to start the year.",[11,3527,3528],{},"Then on the 5th of January I got on a flight to Chile to board a cruise ship where I was going to give a talk while cruising through the waters of Antarctica. It was such an incredible experience traveling to south America and to Antarctica and giving a talk on a cruise ship. My talk was on how I got to where I am today and all the challenges I had to get through to get here so it was a very appropriate talk for the Antarctic cruising.",[11,3530,3531],{},"But it wasn't just the conference on that cruise but the 17 days where I got to spend time with some of the most amazing speakers from around the world. If you have ever been to a speaker dinner you will know what I mean. We had 17 speaker dinners, breakfasts, lunches and more. It was a time to connect with other like minded people, a time to share doubts and a time to gain confidence and different points of views. I really believe that cruise shaped my year to come in so many ways.",[11,3533,3534],{},"Then at the end of January I travelled to Valencia to give a talk on Nuxt at a local meetup which was loads of fun.",[23,3536,3538],{"id":3537},"february","February:",[11,3540,3541],{},"Another goal achieved. I got to speak at the Vue Conference in Amsterdam on that massive stage, in fact I got to close the show which was really cool. Sarah Drasner who is someone I look up to so much got to see my talk and congratulated me on it which meant the world to me. Vue Amsterdam is one of my favourite conferences and it was so cool to just hang out with my many speaker friends as well as my two favourite brothers, Sebastién and Alex, as well as meet the majority of the Nuxt maintainers, who were also at the conference.",[11,3543,3544],{},"I somehow also managed to fit in 2 weekend skiing breaks in Andorra. Am so glad I did.",[23,3546,3548],{"id":3547},"march","March:",[11,3550,3551],{},"Then it was off to Texas which was amazing. This was my last memory of normality. I flew to Texas to speak at the Vue Conf USA, another goal achieved. Again hanging out with my Vue family and speaker friends and getting to meet and hang out other attendees was just so cool. Speaker dinner and partying afterwards and singing karaoke again with Evan You was the last night out I had before things changed. All in all not a bad memory to have.",[11,3553,3554],{},"But we had no idea what was around the corner. While in Texas we got the news that the MVP summit in Washington was cancelled. I had my tickets already but it wasn't going to happen. I had been accepted to attend the Google IO in San Francisco which was also cancelled and I was meant to travel to San Francisco again to attend an event held by Cloudinary which was also cancelled as well as 2 meetups in Switzerland, where I was meant to be spending my birthday, also cancelled. And that was just what was meant to happen in March and April. What had looked like an amazing year was now kinda falling apart.",[11,3556,3557],{},"We got sent home from work in March for what we thought was perhaps a few days. Little did we know that we would be locked up for a very long 7 weeks and not allowed to leave our home, not even for sport or to go for a walk.",[23,3559,3561],{"id":3560},"april","April",[11,3563,3564],{},"I started a new job. Right in the middle of lockdown. Head of Learning and Developer Advocate for NuxtJS. This for me was like a dream come true. Any of you who have met me or have known me for a while will know how passionate I am about Nuxt and how much I have been speaking about Nuxt at the previous conferences as well as teaching Nuxt and using it in my previous jobs. But now to work for Nuxt fulltime, to get paid to work for Nuxt, was just unreal.",[11,3566,3567],{},"Of course changing a job is always stressful and doing it in lockdown was even more so. I didn't even have a good desk and my chair was not good enough for sitting 8 hours a day and although I ordered one, it took more than 2 months to arrive. April was hard as we were not allowed out on the streets, not allowed to do sport or go for a walk, not allowed go to the supermarket with your partner. They were strange and scary times. But I had a new job so decided to just focus on that. Easter holidays? nah, just work.",[11,3569,3570],{},"As we were not allowed out to do sport I went running on my roof terrace and managed to run 10km one day. I would change direction after each km so as not to hurt my ankles with so much running in circles. It kept me sane as well as the virtual Taekwondo classes and then the balcony parties. At 8pm each evening we would all go to our balconies and clap the health care workers and dance to 3 songs. It was a time where we just saw people and heard people and came together and it was nice as you felt not so alone and it reminded you that people existed. We would wave over from one apartment block to the other and dance from one balcony to the other.",[11,3572,3573],{},"Online events started and I spoke at the Vue Amsterdam meetup. This was where I learnt anything can go wrong as I lost internet connection in the middle of my talk. Luckily I managed to connect again through my phone and finish the talk.",[23,3575,3577],{"id":3576},"may","May",[11,3579,3580],{},"May we were allowed have our freedom back. I got to leave my apartment to do sport at set times of the day and for a maximum of 2 hours. I was so happy to finally leave the apartment and just go for a cycle and smell the country air and see things like the sea and fields and mountains and but more than anything see people.",[11,3582,3583],{},"I spoke at more virtual events as every conference was either cancelled, postponed to later in the year or turned into a virtual event. There was a need to reach more people and share knowledge like never before. I got to kick off the Dev around the Sun conference which was a conference organised by good friends of mine to raise money for healthcare workers and people in need during the crazy times we were living in.",[11,3585,3586],{},"In May I got to be part of the Microsoft Build event which was just a few minutes online but it was just so amazing to be part of it and to have been able to collaborate with Microsoft for the Azure static web apps project.",[23,3588,3590],{"id":3589},"june","June",[11,3592,3593],{},"June is when I started to get brave and did my first live stream with James Quick as I taught him how to use Nuxt Content and what I realised is that I really liked live streams. They were so much fun and interactive. Much more than the conference talks which were mostly me talking to a computer screen. Although that month I did do a lightening talk for Hasura Conf which I really enjoyed.",[23,3595,3597],{"id":3596},"july","July",[11,3599,3600],{},"In July I got to appear on Create Frontend, a Microsoft event where I got to talk with Wassim Chegham about why static sites were back. Although this was a great experience I lost control of my computer and couldn't see a thing. I freaked out for about a minute and then just thought ok pretend it's a podcast and carry on, and so I did.",[23,3602,3604],{"id":3603},"august","August",[11,3606,3607],{},"August, which is normally a quiet month for conferences, saw lots of interviews including a GraphQL fireside chat with Tanmai from Hasura. I also got appeared on VueVear where I was interviewed in Spanish and then more people started contacting me for interviews when they realised I could actually speak Spanish fluently.",[23,3609,3611],{"id":3610},"september","September",[11,3613,3614],{},"September was hard. Every conference seemed to think it would be a good idea to hold a conference in September and I had said yes to way too many conferences. Although great fun it was exhausting speaking at so many conferences in a short space of time, from CityJS Conf, VueJS Global, Gotopia, ijs London and Mauritius Dev conf with most of them in the same week.",[11,3616,3617],{},"September is also the month I ended up in hospital for two days. Doctors never found out what was wrong but looking back I think it was stress from all the back to back conferences and just trying to do too much. It was nice to have a few days to just think about things and that is when I made the decision to not do more conferences as to be honest it was getting boring recording yourself talking to a screen. I needed time away from them. I wanted to work on things that were more fun, less stressful and that I got more enjoyment out of.",[23,3619,3621],{"id":3620},"october","October",[11,3623,3624],{},"So October was full of Podcasts and Live Streams and it was a lot of fun. My first live stream that I organised I ended up freezing at the end and couldn't see a thing or use my computer but I could be heard. It was so stressful. Luckily I had Maya Shavin on as a guest and she was able to keep things going for me.",[11,3626,3627,3628,3633],{},"After some encouragement from some friends I started my ",[15,3629,3632],{"href":3630,"rel":3631},"https://www.youtube.com/channel/UCrNvYFsT1L3WczE8AizDQ6g",[19],"youtube channel"," to help people learn Nuxt for free. It was a learning process on how YouTube works, how to create short videos and how to teach and keep it fun at the same time too. But as I got great feedback and comments I decided to keep creating more content and even tried live streams again after doing computer resets and investing in a device that emits the wifi signal further. Also I am still figuring out Twitch. That will have to go into next years plans.",[11,3635,3636],{},"I got to appear on Create Serverless, a Microsoft event, where I was interviewed about Nuxt, and I also wrote a blog post on how we use Nuxt at the NuxtJS Company, which was featured on Microsoft's blog.",[11,3638,3639],{},"I finished recording 2 courses on GraphQL and Vue Apollo, which were published on Vue School.",[23,3641,3643],{"id":3642},"november","November",[11,3645,3646],{},"Although I said I wasn't going to speak at any more conferences, there were two that I couldn't say no to. One of them was Vue Toronto. Although I recorded my talk and messed up the export of sound, which meant it wasn't in stereo. I guess you learn something new with every event you do. I also did a live stream with Alex from Vue School where my computer decided to not charge and I was at 4 percent battery 5 mins before the live stream. I stressed out and managed to call Alex over whatsapp and stall the live stream until I changed the cable, plugged it into another socket and as that seemed to work I had to move the desk to be closer. All in the space of 5 mins and bang we were live. Live streams really can be full on at times. You have no idea what can go wrong and how to fix it and you really are on your own. But I had fun and even did Karaoke online along with a few other speakers and the organisers themselves.",[11,3648,3649],{},"At Vue Toronto I gave my first online workshop, 8 hours in a zoom room. To be honest I was terrified and worried I wasn't good enough to give a workshop. Imposter Syndrome again and me always comparing myself to others. But luckily I have good friends who believe in me and always push me in the right direction and I really enjoyed giving the workshop and felt so glad that I did it as I made myself see that I could do it and do it well.",[11,3651,3652],{},"I spoke at BuildStuff Lithuania which is the first conference I ever spoke at so it brings back fond memories. To be honest this was the conference that people thought would finally be in person but unfortunately that was not the case and therefore another online event it was.",[11,3654,3655],{},"In November I created my first Nuxt Module which for me was a big deal and I was so proud of it because I now feel I understand how Nuxt Modules work. Although for sure I need more practice and may have to create a few more to get more confident with them.",[23,3657,3659],{"id":3658},"december","December",[11,3661,3662],{},"December is the month I became a Github Star. It is just incredible to be part of the Github Star program and hang out on slack with others like me who are creating content and working on open source. And the fact that I was nominated by others to be in the program is just incredible. I really am honoured.",[11,3664,3665],{},"More than 1k subscribers on YouTube. This is so amazing and am so happy to be creating content that people find useful.",[11,3667,3668],{},"I finished the year by recording a new course on Getting Started with Nuxt which will be released very soon. Keep following me on twitter for more details in the new year.",[23,3670,3672],{"id":3671},"its-a-wrap","It's a wrap",[11,3674,3675],{},"And that's it. Just like that the year has ended. It has been a very hard and challenging year but I am so glad to have had the people around me who support me, who believe in me and how push me to be better than I am. This year I focused on my career and studied lots. I learnt lots by pushing my own boundaries and doing things that scared me like live-streams, youTube content and giving workshops. I have improved my Nuxt skills so much and have programmed lots, created lots of demos, written lots of posts and talks, re-written the Nuxt docs and just dived a lot deeper into Nuxt. And at the end of the year I can say I have had lots of fun and I have improved not only in tech skills but I think I have become a better person too.",[11,3677,3678],{},"I want to thank everyone who follows me, everyone who has given me such kind feedback, those how have bought me beers and sponsored me on github, those that encourage me, those that challenge me, those that push me to learn more so that I can teach more, those that believe in me and those that support me when I need it and those that are just around when you feel like there is noone else there. So many of you have made a huge impact on my life this year that I can't possibly name you all here. But I think you all know who you are. And I feel really lucky to have so many friends and followers from all walks of life. Thank you.",[23,3680,3682],{"id":3681},"next-year","Next year",[11,3684,3685],{},"Next year is going to be amazing because even though we will remain virtual for some time to come I think we just know how to deal with it better and with such a supportive network I am ready to deal with anything. I think we have lived through such a hard year that next year will be easier and it will be more fun. I know it. I have so many goals for next year, so many things I want to do and so many things I know I will achieve. 2020 has thought me to be more giving, to be more thankful for what I have, to appreciate the little things and to look out for your self more by spending more time in nature, doing more sport and spending quality time with family as it's not always possible. 2020 has shown me that it is important to live for the now and to do things when you can. To not be afraid and to push yourself and above all to do things that make you happy yet when you need to take time to just be you then that is ok too. With all that in mind I am really looking forward to next year as we go from one lockdown to another, I am staying on the positive side of things and I know it's gonna be an amazing year.",[11,3687,3688],{},"Here's to 2021. Happy New year everyone.",{"title":307,"searchDepth":748,"depth":748,"links":3690},[3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705],{"id":3477,"depth":748,"text":3478},{"id":3521,"depth":748,"text":3522},{"id":3537,"depth":748,"text":3538},{"id":3547,"depth":748,"text":3548},{"id":3560,"depth":748,"text":3561},{"id":3576,"depth":748,"text":3577},{"id":3589,"depth":748,"text":3590},{"id":3596,"depth":748,"text":3597},{"id":3603,"depth":748,"text":3604},{"id":3610,"depth":748,"text":3611},{"id":3620,"depth":748,"text":3621},{"id":3642,"depth":748,"text":3643},{"id":3658,"depth":748,"text":3659},{"id":3671,"depth":748,"text":3672},{"id":3681,"depth":748,"text":3682},"2020-12-30","A look back at what we could say has been an interesting year, challenging in many ways yet somehow has been my most successful year to date.","v1609438516/debbie.codes/blog/Screenshot_2020-12-31_at_18.14.59_sbfsen",{},"/blog/2020-in-review",{"title":3469,"description":3707},"blog/2020-in-review",[3464],"4ZEUeIoG9xx87SAoUyU315WjOmd90Ry2Dy1pliCGh_c",{"id":3716,"title":3717,"body":3718,"canonical":788,"date":4370,"description":4371,"extension":786,"featured":787,"image":4372,"meta":4373,"navigation":790,"ogimage":788,"path":4374,"provider":3460,"published":790,"seo":4375,"stem":4376,"tags":4377,"url":788,"__hash__":4378},"blog/blog/2021-in-review.md","2021 - climbing higher and higher",{"type":8,"value":3719,"toc":4353},[3720,3723,3725,3740,3742,3745,3751,3765,3768,3776,3778,3786,3794,3802,3805,3814,3820,3823,3825,3828,3831,3844,3853,3869,3878,3886,3893,3895,3898,3901,3907,3910,3913,3921,3929,3934,3936,3939,3947,3950,3956,3959,3967,3969,3972,3978,3981,3983,3986,3992,3995,4004,4019,4022,4024,4027,4030,4033,4039,4041,4049,4052,4058,4061,4070,4073,4075,4089,4098,4104,4107,4113,4121,4130,4136,4139,4142,4148,4150,4153,4168,4177,4183,4192,4198,4201,4207,4210,4219,4225,4234,4236,4239,4245,4248,4254,4257,4266,4274,4276,4279,4282,4285,4288,4293,4296,4302,4304,4307,4310,4319,4322,4325,4330,4333,4336,4341,4350],[11,3721,3722],{},"2021, another year over yet I have to say it was a very good year. Although there were still crazy lockdowns and travel restrictions I still managed to do a lot and climb even higher in my career.",[23,3724,3478],{"id":3477},[11,3726,3727,3728,3733,3734,3739],{},"So many achievements, from numerous ",[15,3729,3732],{"href":3730,"rel":3731},"https://debbie.codes/resources/conference-talks",[19],"conference talks"," to ",[15,3735,3738],{"href":3736,"rel":3737},"https://debbie.codes/resources/podcasts",[19],"podcast interviews",", to improving my coding skills, being renewed for MVP, changing jobs and learning a new library, reaching more than 10k followers of Twitter and 3.5k subscribers on YouTube. This year has been amazing in so many ways.",[23,3741,3522],{"id":3521},[11,3743,3744],{},"I started January in Ireland in one lockdown but still I managed to get some epic runs in.",[11,3746,3747],{},[121,3748],{"alt":3749,"src":3750},"me running in wicklow mountains","https://res.cloudinary.com/debsobrien/image/upload/v1640971055/debbie.codes/blog/running-wicklow-mountains_c_n7mvet.jpg",[11,3752,3753,3754,3759,3760,891],{},"Podcast on ",[15,3755,3758],{"href":3756,"rel":3757},"https://podcasts.apple.com/us/podcast/2-debbie-obrien-speaking-to-an-online-audience/id1544906363?i=1000503628662",[19],"Speaking to an online audience"," was released as well as my free course on ",[15,3761,3764],{"href":3762,"rel":3763},"https://explorers.netlify.com/learn/get-started-with-nuxt",[19],"Learning Nuxt for the JamStack explorers site",[11,3766,3767],{},"Unfortunately when I arrived back to Mallorca I ended up getting Covid and spent 2 weeks in bed resting and making sure I was ok before I went back to work and sport. GitHub sent me the most amazing flowers which is just so lovely to receive. Recovery took a bit longer than I would have hoped as my voice just didn't seem to want to come back so recording and podcast interviews etc just had to be cancelled.",[11,3769,3770,3771,891],{},"Although ill I still appeared on ",[15,3772,3775],{"href":3773,"rel":3774},"https://youtu.be/UE64S_ExxY4",[19],"Eddie Jaoude's series of GitHub Stars",[23,3777,3538],{"id":3537},[11,3779,3780,3781,891],{},"February is normally my ski month but there was no skiing this time. There was no Vue Amsterdam in person, only remote where I spoke on ",[15,3782,3785],{"href":3783,"rel":3784},"https://noti.st/debbie/5DYjVv/nuxt-performances",[19],"Nuxt performances",[11,3787,3788,3789,891],{},"I got interviewed by ",[15,3790,3793],{"href":3791,"rel":3792},"https://youtu.be/u7UzYB3PBD4",[19],"OS Weeekends on Nuxt and my background to OpenSource",[11,3795,3796,3797,891],{},"I also got to appear on a Podcast with Brian Douglas on ",[15,3798,3801],{"href":3799,"rel":3800},"https://www.heavybit.com/library/podcasts/jamstack-radio/ep-74-redefining-jamstack-with-debbie-obrien-of-nuxtjs/",[19],"Redefining JAMstack",[11,3803,3804],{},"I was headhunted and ended up having many hours of interviews as I tried to convince Bit that I didn't want or need a new job but after many hours of talks I finally decided to take a risk and leave my job at Nuxt, which was like my dream job, and go work for a company that I didn't know, with people I didn't know and on a product I didn't know. It also meant a new framework and therefore a massive challenge. Was I ready? Not one bit.",[11,3806,3807,3808,3813],{},"I created a ",[15,3809,3812],{"href":3810,"rel":3811},"https://youtu.be/UBXWhmlSV-s",[19],"series of YouTube videos called 'Devs in the Forest'"," where I ran for hours in the forest recording advice for myself and for others.",[11,3815,3816],{},[121,3817],{"alt":3818,"src":3819},"Me running in the Forest","https://res.cloudinary.com/debsobrien/image/upload/v1640965793/debbie.codes/blog/devs-in-the-forest_c_gctzoz.jpg",[11,3821,3822],{},"I got to take part in the Vue Contributors days which was great fun and so good to hang out with all my Vue friends online.",[23,3824,3548],{"id":3547},[11,3826,3827],{},"Leaving Nuxt was hard and I cried when I told Alex Chopin that I was leaving but the fact that Nuxt is open source means I still get to be part of the ambassador program and still get to do Nuxt stuff and be involved with the team.",[11,3829,3830],{},"I started my new Job at Bit and really was thrown in the deep end here. And so my journey to learning React began. It was hard. My brain was trying to consume so much from the new product to React and TypeScript. I had no idea if something was cause of React, cause of TypeScript or was just specific to Bit.",[11,3832,3833,3834,3838,3839,891],{},"I was interviewed by Tim Benniks on the DevRel Roundtable(",[15,3835,3836],{"href":3836,"rel":3837},"https://youtu.be/lSxU_q-8Rrc",[19],") alongside Lucie Haberer. I then appeared on the ",[15,3840,3843],{"href":3841,"rel":3842},"https://youtu.be/xytiKzxfzJ8",[19],"women in tech panel with Maya Shavin and Mandy Kerr",[11,3845,3846,3847,3852],{},"I did a ",[15,3848,3851],{"href":3849,"rel":3850},"https://youtu.be/LbUu3Z-ij5U",[19],"live stream trying to learn React from the React docs"," and myself and a friend, Tim Benniks failed so badly and had to be rescued. It was a fun stream. From that stream Rachael Nabors from the React team reached out to me and I became a beta tester for the new React docs.",[11,3854,3855,3856,3861,3862,3865],{},"I also did a podcast Interview with Lyndsay and Steve from Views on Vue on ",[15,3857,3860],{"href":3858,"rel":3859},"https://viewsonvue.com/147",[19],"my move from Vue to React"," as well as another podcast interview with Tracey Lee on ",[15,3863,3864],{"href":307},"Managing Component Architecture",[15,3866,3867],{"href":3867,"rel":3868},"https://modernweb.podbean.com/e/s08e07-modern-web-podcast-managing-component-architecture-with-debbie-obrien/",[19],[11,3870,3871,3872,3877],{},"CityJs Conf was in march and was lots of fun especially for a virtual conference and I got to give my first talk on Bit, ",[15,3873,3876],{"href":3874,"rel":3875},"https://noti.st/debbie/H1PyTj/building-components-in-harmony",[19],"Building components in Harmony",". Also the MVP Summit, again virtual.",[11,3879,3880,3881,891],{},"And I also spoke at the meetup series of Ask the Expert on ",[15,3882,3885],{"href":3883,"rel":3884},"https://youtu.be/Spt4j_Xq79w",[19],"Going Static in a Dynamic World with Hasura and Nuxt.js",[11,3887,3888,3889],{},"And I received a fantastic gift from GitHub for my contributions to the community in 2020. ",[121,3890],{"alt":3891,"src":3892},"GitHub 2020 Skyline award","https://res.cloudinary.com/debsobrien/image/upload/v1640966325/debbie.codes/blog/GitHub-2020-skyline_c_h8b9na.jpg",[23,3894,3561],{"id":3560},[11,3896,3897],{},"Easter holidays were in April and I just worked the whole holidays cause you know, lockdown and travel restrictions so what was the point in doing anything else. Just as well I love my job.",[11,3899,3900],{},"I became a mentor for the Google Program of Road to GDE and I successfully mentored Julia for 3 months who then went on to be a Google Developer Expert.",[11,3902,3903],{},[121,3904],{"alt":3905,"src":3906},"GDE Mentor gift","https://res.cloudinary.com/debsobrien/image/upload/v1640967712/debbie.codes/blog/road-to-gde_jn0deb.jpg",[11,3908,3909],{},"Vue Conf US was virtual and I gave a Nuxt workshop online which was a crazy experience. Great cause we had so much fun with people from all over the world in a zoom room and I managed to make it extremely hands on and fun for a virtual workshop but I was pretty much very zoomed out by the end of the day.",[11,3911,3912],{},"I also spoke at the CodeMotion conference, again online and this time in Spanish. I also got to speak at the online Storyblok conf.",[11,3914,3915,3916],{},"I got interviewed by Marc Backes on ",[15,3917,3920],{"href":3918,"rel":3919},"https://youtu.be/WlpHiYQGG6k",[19],"What is a Developer Advocate",[11,3922,3923,3924,3928],{},"I received my ",[15,3925,3927],{"href":3492,"rel":3926},[19],"GitHub Stars award"," which was amazing and made me feel like I had one the oscars. So proud to be part of this program.",[11,3930,3931],{},[121,3932],{"alt":3927,"src":3933},"https://res.cloudinary.com/debsobrien/image/upload/v1640966732/debbie.codes/blog/GitHub-stars-award_c_zymmvu.jpg",[23,3935,3577],{"id":3576},[11,3937,3938],{},"In May I spoke at the online CodeMotion conference, English edition. I continued to work on Bit, learn more about themeing components in React and started creating demos and videos.",[11,3940,3941,3942,891],{},"I was interviewed by Santosh on ",[15,3943,3946],{"href":3944,"rel":3945},"https://youtu.be/ItgsipIm9s0",[19],"Bit - A better way to build with modern components",[11,3948,3949],{},"I then got invited to be part of the React Contributors day where I got to digitally meet some of my React heros.",[11,3951,3952],{},[121,3953],{"alt":3954,"src":3955},"zoom call for react contributors day","https://res.cloudinary.com/debsobrien/image/upload/v1640967021/debbie.codes/blog/react-contributors-day_c_rirpnn.jpg",[11,3957,3958],{},"I started with a new cycling club with the intentions to improve my cycling and perhaps make new friends and just mingle with people outside of my digital and virtual life.",[11,3960,3961,3962,891],{},"I also got to be interviewed by Margo McCabe for a podcast on ",[15,3963,3966],{"href":3964,"rel":3965},"https://rss.com/podcasts/harperdb-select-star/200839/",[19],"Developer Advocacy, Navigating Tech, & Frontend Dev",[23,3968,3590],{"id":3589},[11,3970,3971],{},"I got to go to a hotel for the weekend in June which was kinda nice as it had been a while since I had been away. It was just in the north of Mallorca and was for a cycling race. Yes I got to compete in my first ever cycling competition. It was so much fun. I did so badly, I was eliminated for being so slow but I kept going even though they opened the road and I had to watch out for traffic. I managed to overtake some of the girls and made it to the finish line and ended up being back in the race. I think I came 5th last but I was so proud I had managed to finish it.",[11,3973,3974],{},[121,3975],{"alt":3976,"src":3977},"me finishing the cycling race","https://res.cloudinary.com/debsobrien/image/upload/v1640967559/debbie.codes/blog/cycling-race-2_c_bgxsgr.jpg",[11,3979,3980],{},"I was invited to appear on the women of React event which was super cool and very scary as I was so new to React. I then got invited to be a member of the React 18 working group which was very incredible indeed. We got to have meetings with the React team as well as a discord chat open for us to ask questions to the team. I spent a lot of time reading through the many GitHub discussions on React 18.",[23,3982,3597],{"id":3596},[11,3984,3985],{},"July was the month where my cycling improved so much and I even managed to go on a 3 day event cycling with a small bag on my back with the bare minimum needed to stay the night and continue cycling the next day. After a 7 and a half hour day of cycling in 40 degree heat I was absolutely wrecked but wow, 157km, what an achievement. And then we had 2 more days of cycling to be done.",[11,3987,3988],{},[121,3989],{"alt":3990,"src":3991},"map showing cycling route of 157km","https://res.cloudinary.com/debsobrien/image/upload/v1640967938/debbie.codes/blog/cycling-157km_mlczr6.jpg",[11,3993,3994],{},"I got to be part of the Hasura Con Topic table for GraphQL with the founder of Hasura and Natalie from the Vue Core team. I also got my MVP award renewed for another year for all my contributions to the community in 2020.",[11,3996,3997,3998,4003],{},"I was back with Eddie Jaoude being interviewed on ",[15,3999,4002],{"href":4000,"rel":4001},"https://youtu.be/9UHDTz5PCkw",[19],"GitHub Stars Stories - are you the next GitHub Star?"," which was so much fun and hopefully will help others who want to be the next GitHub Star.",[11,4005,4006,4007,4012,4013,4018],{},"I got interviewed for FSJam podcast on ",[15,4008,4011],{"href":4009,"rel":4010},"https://fsjam.org/episodes/episode-35-bit-with-debbie-obrien",[19],"Bit and Nuxt"," as well as on ",[15,4014,4017],{"href":4015,"rel":4016},"https://enjoythevue.io/episodes/71/",[19],"Developer Advocacy from Nuxt to React"," with the cast of Enjoy the View.",[11,4020,4021],{},"I also stated to compete in Padel tournaments which was so much fun even though I wasn't very good at it.",[23,4023,3604],{"id":3603},[11,4025,4026],{},"August was very much a month for sport from cycling to Padel and running as well as lots of work building my ecommerce demo project in Bit and further enhancing my React skills.",[11,4028,4029],{},"I also had my first visitor in person who came to stay with me in. The wonderful Heather Downing from the US where we got to hang out lots and just talk about tech stuff.",[11,4031,4032],{},"But I also received my Microsoft Most Valuable Professional award for second year running.",[11,4034,4035],{},[121,4036],{"alt":4037,"src":4038},"MPV award","https://res.cloudinary.com/debsobrien/image/upload/v1640968277/debbie.codes/blog/mvp-award_c_n2nl02.jpg",[23,4040,3611],{"id":3610},[11,4042,4043,4044,891],{},"In September I got to speak at the Nuxt Nation conference on ",[15,4045,4048],{"href":4046,"rel":4047},"https://noti.st/debbie/FzAU44/nuxtify-your-images",[19],"Nuxtifying your Images",[11,4050,4051],{},"The most incredible swag arrived from GitHub to prepare us for the Nova conference in October. A conferences specifically for GitHub stars and staff. Love being a GitHub Star.",[11,4053,4054],{},[121,4055],{"alt":4056,"src":4057},"GitHub Star swag","https://res.cloudinary.com/debsobrien/image/upload/v1640968461/debbie.codes/blog/github-star-swag_c_evp3ng.jpg",[11,4059,4060],{},"My speaker friend Hannes from Belgium came to visit and again so nice to see people in real life.",[11,4062,4063,4064,4069],{},"And the podcast interview in Spanish ",[15,4065,4068],{"href":4066,"rel":4067},"https://anchor.fm/tu-codigo-me-suena/episodes/05---Pasamos-fro-en-la-Antrtida--mientras-tenemos-un-combate-de-taekwondo-y-vamos-en-bici-e153d1o",[19],"Tu Código me suena with Quique Fdez Guerra"," was released.",[11,4071,4072],{},"I had to travel to Ireland to get my second vaccine so that I could fly to Canada in November. Spain wouldn't allow me to have 2 vaccines as I had had covid but Canada required 2 so Ireland to the rescue. It also meant I got to see family so I was not complaining at all.",[23,4074,3621],{"id":3620},[11,4076,4077,4078,4083,4084,891],{},"In October I got to speak virtually at the JetBrains conf on ",[15,4079,4082],{"href":4080,"rel":4081},"https://noti.st/debbie/XZZu3p/scaling-team-delivery-using-standardisation",[19],"Scaling Team Delivery using Standardisation"," as well as React Advanced conf, my first time giving a talk at a React event on ",[15,4085,4088],{"href":4086,"rel":4087},"https://noti.st/debbie/PedGcd/taking-component-driven-one-step-further",[19],"Taking Component Driven One Step Further",[11,4090,4091,4092,4097],{},"I also got to not just attend but speak at the GitHub Nova conference where I spoke on ",[15,4093,4096],{"href":4094,"rel":4095},"https://noti.st/debbie/SUDdPd/building-a-community-with-github-discussions",[19],"Building a community with GitHub Discussions",". I was terrified to speak in front of so many stars but I had great encouragement from Anisha of the GitHub Stars team who helped me overcome my imposter syndrome and just go for it. And I am so glad I did. I also got to virtually meet a real Astronaut and experience my first virtual DJ session which was so much fun. And the swag we received for the Nova conference was just incredible. Love being a GitHub Star.",[11,4099,4100],{},[121,4101],{"alt":4102,"src":4103},"zoom call with an astronaut","https://res.cloudinary.com/debsobrien/image/upload/v1640968883/debbie.codes/blog/github-nova-astronaut_c_snwb4g.jpg",[11,4105,4106],{},"I became a mentor for the Google Women Developer academy and mentored the amazing Liangda Wang who really has an incredible career ahead of herself. I also got to have 1-1 calls with vaious amazing women from around the world and help them overcome imposter syndrome and perhaps become speakers but mainly just believe in themselves more and let them know that we all believe we are not good enough and not ready. Sometimes I really need to listen to my own advice.",[11,4108,4109],{},[121,4110],{"alt":4111,"src":4112},"Women TechMakers thank you note","https://res.cloudinary.com/debsobrien/image/upload/v1640964318/debbie.codes/blog/women-techmakers_jha4ip.jpg",[11,4114,4115,4120],{},[15,4116,4119],{"href":4117,"rel":4118},"https://frontendheroes.transistor.fm/episodes/ant-man-and-the-wasp-w-debbie-obrien",[19],"Podcast interview on Frontend Heros"," with Ant-man and the Wasp, Scott Francis and Evan Payne on Bit.",[11,4122,4123,4124,4129],{},"Real life conferences are back. Oh yea. I got to fly to Nantes in France for my first in person conference with a massive room with lots of people to give a talk on ",[15,4125,4128],{"href":4126,"rel":4127},"https://noti.st/debbie/wsznqq/component-driven-repo-for-component-driven-world",[19],"Component Driven Repo for Component Driven World",". All with masks on but I could see their eyes and hear their claps and they asked questions in person and it was so nice to be back on the stage.",[11,4131,4132],{},[121,4133],{"alt":4134,"src":4135},"on stage at Dev Fest Nantes conf","https://res.cloudinary.com/debsobrien/image/upload/v1640969179/debbie.codes/blog/devfest-nantes_c_yqcsew.jpg",[11,4137,4138],{},"I finally took a few days off work and actually went on holidays. I went to Ireland with my husband for my dads birthday but also hired a car and travelled to parts of my country I had never been to, co Kerry. It was so remote, so safe and just so beautiful and quite nice to actually have some time away from the computer.",[11,4140,4141],{},"And when I got home I was welcomed with a lovely gift from GitHub for the work I do for the community. Now this is just amazing and so nice to receive things like this.",[11,4143,4144],{},[121,4145],{"alt":4146,"src":4147},"github stars gift","https://res.cloudinary.com/debsobrien/image/upload/v1640969399/debbie.codes/blog/github-stars-gift_c_ksh6n1.jpg",[23,4149,3643],{"id":3642},[11,4151,4152],{},"The US was still not fully open so I wasn't able to travel to Connect Tech conf in Atlanta which was an in person event. I spoke at the React India conference which was virtual and pre-recorded but with Q&A.",[11,4154,4155,4156,4161,4162,4167],{},"I was interviewed ",[15,4157,4160],{"href":4158,"rel":4159},"https://youtu.be/n5UpjCWVQAU",[19],"Composing Modern Applications with Bit with Alex Patterson, Brittney Postman"," as well as my journey from ",[15,4163,4166],{"href":4164,"rel":4165},"https://youtu.be/70b-gtHkOZ8",[19],"From Vue to React"," with James Q Quick.",[11,4169,4170,4171,4176],{},"I had a film crew follow me around while cycling the mountains of Mallorca so as to create a ",[15,4172,4175],{"href":4173,"rel":4174},"https://www.youtube.com/watch?v=jf8NbVt4yRg&feature=youtu.be",[19],"promotional video for the Vue Toronto Conf",". This was such an amazing experience.",[11,4178,4179],{},[121,4180],{"alt":4181,"src":4182},"camera man and me while cycling","https://res.cloudinary.com/debsobrien/image/upload/v1640969579/debbie.codes/blog/cycling-film-crew_c_tiiubz.jpg",[11,4184,4185,4186,4191],{},"I did however travel to Lithuania for the Build Stuff Conf which was a hybrid conference with some speakers in person and some remote. Masks and limited attendees made it a smaller conference but to get to hang out with my amazing speaker friends after such a long time was just amazing. My talk ",[15,4187,4190],{"href":4188,"rel":4189},"https://noti.st/debbie/IfdQT6/component-driven-repo-for-a-component-driven-world",[19],"Component Driven Repo for a Component Driven World"," was about architecture in the Frontend world.",[11,4193,4194],{},[121,4195],{"alt":4196,"src":4197},"speaking on stage at build stuff conf","https://res.cloudinary.com/debsobrien/image/upload/v1640970058/debbie.codes/blog/buildstuff-conf_c_bpgvzh.jpg",[11,4199,4200],{},"I then flew to Toronto to MC the Vue Conf Toronto with Henri Helvetica. The conference was online but we were in person in the studio and it was a very long and tiring day but so much fun with some speakers being live online meaning we could talk to them and ask them questions which made it more real. And I have to say Jilson and the organizers looked after me so much and I enjoyed many meals out including one at the CN revolving Tower restaurant which was a cool experience. I also gave an online Nuxt workshop while in Toronto which again was a lot of fun.",[11,4202,4203],{},[121,4204],{"alt":4205,"src":4206},"vue conf toronto studio","https://res.cloudinary.com/debsobrien/image/upload/v1640970168/debbie.codes/blog/vue-conf-studio_c_hsbqdc.jpg",[11,4208,4209],{},"I was asked to speak at the React Conf and had the pleasure of not only working with Rachael Nabors and Ricky Hanlon from the React team but I also got to virtually meet Dan Abramov on a feedback call for my talk where we discussed how I could change it to make it an even better talk. This did mean that while I was travelling I was also working on my talk for the React conf and constantly getting feedback from the team which was very cool and just a great experience.",[11,4211,4212,4213,4218],{},"I then recorded my talk, ",[15,4214,4217],{"href":4215,"rel":4216},"https://noti.st/debbie/DaH9ou/things-i-learnt-from-the-new-react-docs",[19],"Things I learnt from the New React Docs"," between travelling from one Conference to another. This was by far my best experience for virtual conferences as the React team had sent out a suitcase of recording material and they had a professional team call me and help setup everything and then record the talk which we recorded about 4 times cause you know you always just want it to be better.",[11,4220,4221],{},[121,4222],{"alt":4223,"src":4224},"speaking virtually at react conf","https://res.cloudinary.com/debsobrien/image/upload/v1640970262/debbie.codes/blog/react-conf_c_po1euk.jpg",[11,4226,4227,4228,4233],{},"I then flew to Alicante to catch up with Alex Jover from ",[15,4229,4232],{"href":4230,"rel":4231},"https://vuedose.tips/",[19],"Vue Dose"," before setting off to Oslo.",[23,4235,3659],{"id":3658},[11,4237,4238],{},"I flew to Oslo and got to speak at my first NDC conference which was out of this world. 1,200 attendees spread over 7 rooms including an overflow room where you can watch all talks at once with headphones on and just switch to hear the one you want. I gave my talk on a floating stage separated by noise reducing curtains in a venue that holds almost 10,000 people. It was very cool indeed. The conference was amazing, so well organized with free food all day long. But after I gave my talk new restrictions kicked in from the Norwegian government and the final day of the conference was limited to 100 people only. Ah well.",[11,4240,4241],{},[121,4242],{"alt":4243,"src":4244},"me at ndc conf","https://res.cloudinary.com/debsobrien/image/upload/v1640970413/debbie.codes/blog/ndc-conf_c_msgccn.jpg",[11,4246,4247],{},"A few speaker friends had arranged a cabin in the mountains and for the weekend we headed to this amazing cabin that sleeps 15 people with its own sauna and so much snow. We had so much fun just chatting and hanging out and even got to go skiing together. It was a lovely finish to an amazing few weeks of traveling and events.",[11,4249,4250],{},[121,4251],{"alt":4252,"src":4253},"skiing with speaker friends","https://res.cloudinary.com/debsobrien/image/upload/v1640970440/debbie.codes/blog/skiing-norway_c_ggj5hu.jpg",[11,4255,4256],{},"The React conf was live and it was so nice to see my talk at the conf and answer the questions in the chat but most of all just receive the amazing feedback that I got. The fact that a few months ago I had just started learning React and now I was speaking at the React Conference and getting amazing feedback from Dan Abramov was just an amazing achievement for me.",[11,4258,4259,4260,4265],{},"I finished my ",[15,4261,4264],{"href":4262,"rel":4263},"https://bit.dev/learn-bit-react/shoe-store/apps/shoe-store/~compositions",[19],"ecommerce demo"," complete with shopping cart that worked saving products in state and using context. My React skills had improved so much and I felt so much more comfortable writing React code although breaking the cart down to decoupled and reusable components was still a lot for me to take in and process. But with help I managed to achieve it and I was proud of ending the year with an amazing demo coded by me.",[11,4267,4268,4269,891],{},"When I say finished, it is not finished of course and so much more to do but finished in that I got done what I wanted to get done and am so proud of that. Here it is ",[15,4270,4273],{"href":4271,"rel":4272},"https://bit-shoe-store.netlify.app/",[19],"deployed on Netlify",[23,4275,3672],{"id":3671},[11,4277,4278],{},"And another year over already. This year went by so fast. It had so many challenges from travel restrictions and lockdowns to changing jobs and frameworks. I doubted so much about changing jobs, was I ready? would I be able to learn and be good at React and would I be welcomed in the React community? I am so glad I overcame my own fear and just took that step into the unknown. By doing this I pushed my own boundaries and improved my coding skills by so much.",[11,4280,4281],{},"I spoke at lots of conferences and I don't know how but I managed to just about escape from burn out but believe me I was close to it especially just before traveling to in person conferences. I was trying to build stuff, write talks and travel in a world that was full of so many uncertainties. I do think that going forward I need to say no to conferences and not yes to everything and make sure mental health comes first cause it is so easy to say yes, take on too many things and then have the pressure of having to deliver. You always do it cause it's good for your career and cause you don't want to let people down but I mentored some amazing women this year and I know there are other people out there that might love to get the chance to speak at a conference so perhaps it is time to not say no but say I can't but I know someone who can. Perhaps even mentor and help them along the way. Who knows. We shall see. I am still learning to say no so wish me luck on that one.",[11,4283,4284],{},"I got to be interviewed this year for so many podcasts and I had so much fun chatting with so many people about various things. I managed to get past 10k followers on Twitter which for me is just cool cause I live in one of the smallest islands in the world so making it in the big time is like a big deal for me.",[11,4286,4287],{},"Last year I said I wanted to do more sport and I certainly did that. I took more time for myself and spent a lot of time outdoors and in the mountains which not only improved my fitness but also my mental health and doing sport with other people, real people, no offence to my virtual friends but sometimes real people are needed.",[1713,4289,4290],{},[11,4291,4292],{},"I am so glad I got to travel again, meet my speaker friends again, be on a stage again and just have fun with people. I think I needed that.",[11,4294,4295],{},"And as I went for my last run of the year in shorts and t-shirt because it was so hot I thought to myself how this year had been a pretty good year even though there were obstacles to overcome and different paths to choose from and to be honest I think it's better that way. I love being challenged, it means I am pushing myself, growing and becoming better at what I do, be that in tech, in sport or in anything else.",[11,4297,4298],{},[121,4299],{"alt":4300,"src":4301},"me running in Ireland","https://res.cloudinary.com/debsobrien/image/upload/v1640970637/debbie.codes/blog/running-in-ireland_c_drlsge.jpg",[23,4303,3682],{"id":3681},[11,4305,4306],{},"What on earth is going to happen next year? Who knows. But I have learnt something very valuable. Life is very short so you really do have to take every opportunity as it arises.",[11,4308,4309],{},"Or in the words of the late Abel Wang (still can't believe he is gone, such an amazing guy taken too soon),",[1713,4311,4312],{},[11,4313,4314,891],{},[15,4315,4318],{"href":4316,"rel":4317},"https://youtu.be/yPfMW0CZpms",[19],"Don't accept the defaults",[11,4320,4321],{},"We live our lives a lot of time just accepting the defaults and then things don't happen, things don't change. Defaults are boring. Let's stop being boring. Let's live on the edge. Let's do things cause tomorrow might not come.",[11,4323,4324],{},"When I have to make a decision I always think what if next year was my last year, what have I not done that I want to do and what can I do next year to make that happen. Life is short. We need to live it, we need to improve in our careers, we need to be happy and have fun and do sport. We need to find the right balance. I think I did a pretty good job of it this year so I hope next year I can do the same.",[1713,4326,4327],{},[11,4328,4329],{},"Avoiding burnout will probably be my biggest challenge for next year.",[11,4331,4332],{},"And of course I want to improve so much. I want to improve my coding skills so much more and be known as an amazing engineer. I do want to travel more of course and I hope the world allows us to safely do that. I want to meet all the people from all the amazing programs I am part of but above all I want to help more people. I see so many people unhappy in life and I just know that they can do better. I know that tech is not for everyone but I think a lot of people can have a much better life if they give it a go.",[11,4334,4335],{},"And yet again I want to overcome imposter syndrome. I am getting stronger and being surrounded by such amazing people in tech really helps me. I will get better at this and one day I will just say I am here and I am ready but until then I will be so scared and and have to fight with myself to make myself believe I can do it and I will think I am going to fail and will be so afraid of that. I need to not be afraid of that and I need to not be so hard on myself.",[1713,4337,4338],{},[11,4339,4340],{},"We are all different, we all learn at different paces and we are all good at different things and you know what, it's actually good to be different.",[11,4342,4343,4344,4349],{},"I will admit I am pretty happy with my life right now and that puts me in a very lucky place but for those that know me that know ",[15,4345,4348],{"href":4346,"rel":4347},"https://youtu.be/Be3N_i4bC34",[19],"my story",", will know that this hasn't always been the case and I know what it is like to be miserable, poor and see no future. I amaze myself every day when I look at how far I have come and I just know that so many other people can also make it just like I did. So here is to 2022. Whatever it has in store for us, don't accept the defaults and go take risks and just live, live like there is no tomorrow, grow in your career and have fun at the same time. oh and sport, you gotta do sport.",[11,4351,4352],{},"Here's to 2022. Happy New year everyone.",{"title":307,"searchDepth":748,"depth":748,"links":4354},[4355,4356,4357,4358,4359,4360,4361,4362,4363,4364,4365,4366,4367,4368,4369],{"id":3477,"depth":748,"text":3478},{"id":3521,"depth":748,"text":3522},{"id":3537,"depth":748,"text":3538},{"id":3547,"depth":748,"text":3548},{"id":3560,"depth":748,"text":3561},{"id":3576,"depth":748,"text":3577},{"id":3589,"depth":748,"text":3590},{"id":3596,"depth":748,"text":3597},{"id":3603,"depth":748,"text":3604},{"id":3610,"depth":748,"text":3611},{"id":3620,"depth":748,"text":3621},{"id":3642,"depth":748,"text":3643},{"id":3658,"depth":748,"text":3659},{"id":3671,"depth":748,"text":3672},{"id":3681,"depth":748,"text":3682},"2021-12-31","A look back at yet again another amazing year even though times were hard. From lockdowns to in person conferences, to changing jobs and learning a new library, as well as mentoring, teaching workshops, doing lots of sport and so much more.","v1640964235/debbie.codes/blog/cycling-sa-colobra_mvtntg.jpg",{},"/blog/2021-in-review",{"title":3717,"description":4371},"blog/2021-in-review",[3464],"CRBi0fQ67nn3FfyYc4kqOxbssOzwZNsGr_R80dFL6FM",{"id":4380,"title":4381,"body":4382,"canonical":788,"date":4807,"description":4808,"extension":786,"featured":787,"image":4809,"meta":4810,"navigation":790,"ogimage":788,"path":4812,"provider":3460,"published":790,"seo":4813,"stem":4814,"tags":4815,"url":788,"__hash__":4816},"blog/blog/2022-in-review.md","2022 Recap - Achieving your dreams Debbie",{"type":8,"value":4383,"toc":4790},[4384,4387,4389,4398,4402,4405,4411,4414,4420,4423,4426,4429,4435,4439,4442,4448,4451,4455,4458,4464,4467,4470,4473,4477,4480,4486,4489,4492,4495,4501,4504,4507,4513,4516,4520,4523,4526,4532,4535,4541,4544,4548,4551,4554,4560,4563,4566,4572,4576,4579,4585,4588,4591,4595,4598,4604,4608,4611,4614,4617,4623,4626,4629,4635,4639,4642,4645,4651,4654,4657,4663,4666,4669,4675,4678,4681,4687,4691,4694,4697,4701,4704,4707,4714,4717,4720,4723,4729,4731,4734,4742,4745,4753,4755,4758,4761,4763,4766,4769,4772,4775,4778,4781,4784,4787],[11,4385,4386],{},"2022 the first year of proposed normality and back to in person conferences and traveling, interviewing for the big companies and getting hired by Microsoft makes 2022 an incredible year that I will never forget.",[23,4388,3478],{"id":3477},[11,4390,3727,4391,3733,4394,4397],{},[15,4392,3732],{"href":4393},"/videos",[15,4395,3738],{"href":4396},"/podcasts",", to improving my coding skills, being renewed for Google Developer Expert program, changing jobs and veering into the testing scene, reaching more than 15k followers on Twitter and 4.69k subscribers on YouTube. This year has been an incredible year.",[23,4399,4401],{"id":4400},"jan-it-began-with-running-and-interviews","Jan: It began with running and interviews",[11,4403,4404],{},"I started the year with a beautiful scenic, muddy run with my brother along the Wicklow coast. It's not just about the running but the chats on the way reflecting on the last year and looking forward to the new one with new goals, hopes and dreams.",[11,4406,4407],{},[121,4408],{"alt":4409,"src":4410},"Wicklow Coast Run","https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673858048/debbie.codes/blog/2022/wicklow-running_fptw0q.jpg",[11,4412,4413],{},"Ski holidays were back, with masks on chair lifts even though we were out in the open but sure, ok. We had a great few days skiing with amazing snow.",[11,4415,4416],{},[121,4417],{"alt":4418,"src":4419},"ski holiday","https://res.cloudinary.com/debsobrien/image/upload/v1673858280/debbie.codes/blog/2022/skiing_ezzxth.jpg",[11,4421,4422],{},"Things were not going to well in my job and I wasn't allowed to do what I do best or make any decisions. Managers didn't turn up for 1-1 meetings and it made me feel unvalued and unimportant. The company were simply not ready to push dev rel to the next stage and I needed to do more. So I started interviewing with a few companies which opened my eyes to new opportunities. It also helped me see what I didn't want.",[11,4424,4425],{},"I started the interview process for Google after a let down from Facebook. I had no idea what I was getting myself into as I just presumed it would amount to nothing but next thing I knew I was going for the actual Google interview. And to do that I needed to study Computer Science. I had never studied computer science. So I started with signing up for the Harvard CS50 degree and I studied a lot.",[11,4427,4428],{},"At the end of January I traveled to Menorca, one of the smaller islands of the Balearic chain where I gave my talk on Imposter Syndrome. Due to covid fears the Government decided against the college students attending in person and most of our audience was therefore online and we remained fully masked for the whole event, even when giving my talk. But we had a nice small audience and it was still good fun and I got to spend the weekend visiting the rest of the island with my husband.",[11,4430,4431],{},[121,4432],{"alt":4433,"src":4434},"menorca conf","https://res.cloudinary.com/debsobrien/image/upload/v1673858389/debbie.codes/blog/2022/menorca_g1qaan.jpg",[23,4436,4438],{"id":4437},"feb-sport-and-more-interviews","Feb: Sport and more interviews",[11,4440,4441],{},"February was a great month for cycling and running, playing padel, studying and more interviews. I studied lots but when it came to taking the main Google whiteboard Interviewed I failed. I knew I failed straight away as I just didn't do my best. The whole process through me off and I blanked out on things I know, things I have made video courses on and taught at workshops. My brain just didn't function the way it should have. And there is nothing more than a failed coding challenge to make you feel very useless.",[11,4443,4444],{},[121,4445],{"alt":4446,"src":4447},"running in mountains","https://res.cloudinary.com/debsobrien/image/upload/v1673858752/debbie.codes/blog/2022/running-trail_qjlb9h.jpg",[11,4449,4450],{},"However it did set me up for my next challenge. My first interview with Microsoft. Now having experienced the Google process I was terrified that the Microsoft one would be the same. Yet it was not and instead of having to study algorithms and data structures I had to instead study the product itself, Playwright.",[23,4452,4454],{"id":4453},"march-studying-conferences-and-interviews","March: Studying, conferences and Interviews",[11,4456,4457],{},"March was a trip to the Canaries for a friends wedding. I had never been to the Canary islands and they got married in a banana plantation which was very cool. We also traveled the island by car, plus lots of running of course, and got to see some amazing mountain views and incredible sand dunes.",[11,4459,4460],{},[121,4461],{"alt":4462,"src":4463},"canary islands","https://res.cloudinary.com/debsobrien/image/upload/v1673858975/debbie.codes/blog/2022/canaries_dkkzgg.jpg",[11,4465,4466],{},"I spent so much time studying and preparing for my Microsoft interview that I didn't celebrate Saint Patrick's day at all. Not even a single beer. I just knew that I had to give it my all so every second I had to spare was spent studying and preparing for the interview. And it was worth it cause the interview went really well and I was offered the job a few weeks later much to my disbelief.",[11,4468,4469],{},"I traveled to Amsterdam for a React conference and arrived a few days early and got to hang out with Alex from Vue School and also meet Gift in person as well as go for some nice runs. I had planned to bring running gear with me and go for a run in every city I visited at every conference I spoke at, no matter what month it was.",[11,4471,4472],{},"I gave a remote talk from the hotel on Components, for a company conference in Germany which went really well. Then it was speaker dinner time where I got to hang out with and meet some really cool people from the React community.",[23,4474,4476],{"id":4475},"april-conferences-and-the-job-offer","April: Conferences and the job offer",[11,4478,4479],{},"It snowed in Amsterdam. It was so beautiful. I gave my talk at React Conf that morning and it was so nice to be back on that big stage. I also got interviewed for a podcast while there. I had to leave by lunch time to get a flight to Belfast to attend a hen party for my sister-in-law. We spent the weekend in Belfast out drinking and celebrating as well as lots of running. Yes I was the only one who got up in the morning and went for run.",[11,4481,4482],{},[121,4483],{"alt":4484,"src":4485},"react amsterdam","https://res.cloudinary.com/debsobrien/image/upload/v1673859211/debbie.codes/blog/2022/react-amsterdam_zsqn44.jpg",[11,4487,4488],{},"I then traveled down to my home town in County Wicklow in Ireland to stay with family and work remotely for a week so I could get to meet the newest member of my family, my new niece as well as spend my birthday with my family. Weather was fantastic so I got to have plenty of country runs.",[11,4490,4491],{},"It was here that I took the call for my offer for Microsoft, right in my dads bar. Yes, there is a bar in my parents house, a very nice one too. So I got to celebrate my new job offer with my family.",[11,4493,4494],{},"GitHub sent me out my year in 2021 GitHub contributions in 3d format with a thank you note and a congratulations on what I brought to 2021. It was so cool to compare it to last year and see how much more I have contributed in code.",[11,4496,4497],{},[121,4498],{"alt":4499,"src":4500},"github skyline","https://res.cloudinary.com/debsobrien/image/upload/v1673861661/debbie.codes/blog/2022/github-skyline_wsewwn.jpg",[11,4502,4503],{},"There was also lots of running and cycling in April as well as padel as my goal to do 3 hours of sport a day was becoming a reality.",[11,4505,4506],{},"I then traveled to Verona, in Italy to give a talk at a conference I have been to before but is one I love very much. I was surprised at the lack of diversity in the audience and brought it up at the start of my talk and got a huge clap in support from the guys. Then a lovely long run around Verona.",[11,4508,4509],{},[121,4510],{"alt":4511,"src":4512},"js verona","https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673859608/debbie.codes/blog/2022/js-verona_mxwqjt.jpg",[11,4514,4515],{},"On the last day of April I flew to Barcelona to take Natalia Venditto out for lunch, as she was the one who told me about the job in Microsoft and had recommended that I apply. We met up with Alvaro while there and then I flew home that evening.",[23,4517,4519],{"id":4518},"may-conferences-starting-in-microsoft-and-visiting-the-offices-in-seattle","May: Conferences, starting in Microsoft and visiting the offices in Seattle",[11,4521,4522],{},"May was playing a padel tournament with a torn ligament which was not ideal. Rest days. Debbie doesn't do rest days. I had finished my job at Bit and had some time off to recharge batteries before starting at Microsoft. On the 11th of May when the contract came through I was so excited. This was making it feel even more real.",[11,4524,4525],{},"Then it was my last conference talk from my previous job at BeJS Brussels. This is a great local conference where I wrote about diversity in a post as I was pleasantly surprised at how diverse the conference was. I got to meet some great people there and had a lovely tour around the Belgian cities.",[11,4527,4528],{},[121,4529],{"alt":4530,"src":4531},"me and speakers panel at BEJS","https://res.cloudinary.com/debsobrien/image/upload/v1673859773/debbie.codes/blog/2022/bejs-panel_surbtt.jpg",[11,4533,4534],{},"I stared work for Microsoft on the 16th of May as the 15th was a Sunday and on the 21st of May I got to fly to Seattle to meet the team and visit the Microsoft offices. You can not even imagine how excited I was. It was incredible, especially when I found there was a running track around the campus. I literally went for a run every single day.",[11,4536,4537],{},[121,4538],{"alt":4539,"src":4540},"microsoft campus","https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673861981/debbie.codes/blog/2022/microsoft-sign_kieb6v.jpg",[11,4542,4543],{},"It was so nice to meet my team and hang out and get to know each other and also to meet and hangout with Scott Hanselman, whom I had always looked up to and was now someone I worked for. He is such a nice person in real life and not just saying that cause I work for him. I also met other people from different teams and spent time enjoying the campus from the offices to the studios, shops and the tree houses. I sat in those tree houses for ages just thinking how lucky I was to be here and wondering if it was really just all a big dream.",[23,4545,4547],{"id":4546},"june-conferences-and-a-music-festival","June: Conferences and a music festival",[11,4549,4550],{},"In June I went to Budapest for a meetup and a conference in a railway museum which was lots of fun. It was my first time giving a talk on Playwright and as a Microsoft employee and I can't really explain it but it gave me a new sense of confidence that I didn't have before. I also got to have dinner with speaker friends and do plenty of networking.",[11,4552,4553],{},"Then it was Miami for Vue conf where I got to fly to Miami for a week to give a Nuxt workshop. I managed to through some testing onto the end of the workshop and I gave a lightening talk on Playwright. I also got to meet up with some friends from the Vue community and hang out with them. Of course there was more running even though it was so hot.",[11,4555,4556],{},[121,4557],{"alt":4558,"src":4559},"nuxt workshop","https://res.cloudinary.com/debsobrien/image/upload/v1673860104/debbie.codes/blog/2022/nuxt-workshop_xflqgh.jpg",[11,4561,4562],{},"I got to attend my first music festival. Yes I am new to music festivals. It was great and I really enjoyed it especially Franz Ferdinand and Cristina Aguilera.",[11,4564,4565],{},"My first .NET conference was in Madrid where I gave my talk on Playwright and worked on the Microsoft booth telling the world about Playwright and how cool it is.",[11,4567,4568],{},[121,4569],{"alt":4570,"src":4571},"dotnet madrid conference","https://res.cloudinary.com/debsobrien/image/upload/v1673856374/debbie.codes/blog/2022/dotnet-madrid_agwxxy.jpg",[23,4573,4575],{"id":4574},"july-conferences-and-sport","July: Conferences and sport",[11,4577,4578],{},"In June I got to visit the Microsoft offices in Barcelona and collect my badge before attending Vue Road trip where I gave my talk on Playwright and got to hang out with more members of the Vue community. Of course running in Barcelona followed by breakfast with speaker friends.",[11,4580,4581],{},[121,4582],{"alt":4583,"src":4584},"my with by micorsoft badge","https://res.cloudinary.com/debsobrien/image/upload/v1673856285/debbie.codes/blog/2022/microsoft-offices-bcn_cui5eh.png",[11,4586,4587],{},"July in general was a quiet month and therefore I got to spend more time improving my skills and working on documentation.",[11,4589,4590],{},"And of course lots of sport from padel to cycling and swimming again trying to keep up my goal of 3 hours of sport a day which I was easily achieving.",[23,4592,4594],{"id":4593},"august-holidays-and-family-time","August: Holidays and family time",[11,4596,4597],{},"August was sport and holiday month. I worked the first few weeks of the month and then flew to Greece for my brothers wedding. I took a few days to do a road trip on the mainland visiting Meteora and then flew to the island of Corfu where I had a fantastic time with my family from parents, brothers, sisters, nieces, nephews, cousins, aunts, uncles and friends. We all had such a great time and the first time we had all been together since covid times. And yes lots of running of course as well as swims and a boat trip which was epic.",[11,4599,4600],{},[121,4601],{"alt":4602,"src":4603},"meteora greece","https://res.cloudinary.com/debsobrien/image/upload/v1673860520/debbie.codes/blog/2022/meteora-greece_wqumec.jpg",[23,4605,4607],{"id":4606},"september-conferences-and-the-gran-fondo","September: Conferences and the gran fondo",[11,4609,4610],{},"Back to work and conference season started with a talk in Spanish on Playwright for Midu Conf live on Twitch with over 10,000 live viewers.",[11,4612,4613],{},"Next was off to Croatia for Inforbip Shift conference. This was an amazing conference in so many ways. So many young people hungry for knowledge and such a diverse conference. I gave my talk on Playwright as well as interviewed for a podcast and the local news and helped on the Microsoft booth.",[11,4615,4616],{},"I also got to do tons of networking and showing people Playwright and chatting all things testing with Kent C. Dodds over lunches and swims in the sea. And I got to do lots of running by the Croatian coastline as the hotel was set in such a beautiful area. I can't mention all the amazing people I met as there were just too many and I can't explain to you how beautiful the stars were as it has to be seen to be believed.",[11,4618,4619],{},[121,4620],{"alt":4621,"src":4622},"me and Kent C. Dodds","https://res.cloudinary.com/debsobrien/image/upload/v1673856445/debbie.codes/blog/2022/me-kent-c-dodds_yj4cnn.jpg",[11,4624,4625],{},"Next up was Oslo for NDC. It was so good to be back for this conference where I gave my talk on Playwright and again worked on the booth to show Playwright off to everyone that came by the booth and give away Playwright stickers. Lots of networking with other speakers and got a few runs in too plus got to hang out with Jayme one of my work colleagues.",[11,4627,4628],{},"September was also the month of the Gran Fondo. I cycled 200km in one day round the island of Mallorca. This was an amazing challenge for me. It was very tough and I cycled most of it on my own as couldn't keep up with the others making it even more rewarding. It was an incredible experience.",[11,4630,4631],{},[121,4632],{"alt":4633,"src":4634},"gran fondo mallorca","https://res.cloudinary.com/debsobrien/image/upload/v1673860709/debbie.codes/blog/2022/gran-fondo_dmnecs.jpg",[23,4636,4638],{"id":4637},"october-conferences-and-family-time","October: Conferences and family time",[11,4640,4641],{},"October was a busy month for traveling and conferences. I planned things so that instead of flying back to Mallorca I stayed in Ireland which meant I got to spend weekends with my family and was closer to the conferences so short flights and less travel time.",[11,4643,4644],{},"I flew from Ireland to Berlin where I got to not just meet and hang out with Max Schmitt from the Playwright team. We got to have lunch, dinner, coffee and even go for an amazing run around the city of Berlin. Then the next day I spoke at the Vue Conf and gave my talk on Playwright.",[11,4646,4647],{},[121,4648],{"alt":4649,"src":4650},"Max and I running around Berlin","https://res.cloudinary.com/debsobrien/image/upload/v1673856210/debbie.codes/blog/2022/max-me-berlin_wx5q4a.png",[11,4652,4653],{},"I had been double booked as the conference had changed its dates so I ended up leaving the conference at lunch time and flew to Stockholm for Nordic JS where I arrived right at speaker dinner which was kinda surreal. But I got to hangout and network with some great speakers.",[11,4655,4656],{},"I gave my Playwright talk to over 1,000 people. It was a great conference and the audience were so much fun. I had taken advice from a good speaker friend on how I could improve as a speaker and I was starting to apply it and getting great results and was super happy with how my talk went giving me more confidence as a speaker. Sweeden was great fun and networked and chatted Playwright with so many people and it was here I went for a run around Stockholm and before I knew it had run 24km. It was just such a beautiful city. I will for sure be back.",[11,4658,4659],{},[121,4660],{"alt":4661,"src":4662},"Nordic JS","https://res.cloudinary.com/debsobrien/image/upload/v1673856555/debbie.codes/blog/2022/nordic-js_tycyhz.jpg",[11,4664,4665],{},"Next up was Technorama Conference in Utrecht. Here I got to hang out with my boss James Montemagno as well as other amazing speakers. It was a multi track conference but was happy to know that my room was full and that they had to turn people away. I had a small crowd who were great fun and asked so many questions during and after my talk making it very dynamic and engaging. I also got to work on the Microsoft booth telling everyone about Playwright and how awesome it was. And of course got to go on some amazing runs by the canals and through the town.",[11,4667,4668],{},"From Utrecht I took a train to Belgium to give my talk at React Belgium. It was nice to be back in Brussels and give my Playwright talk here and help out on the Microsoft booth. And of course more running, this time I managed to get some other speakers to come along too. Lots of nice dinners with speaker friends and chocolates. Who doesn't love Belgian chocolates.",[11,4670,4671],{},[121,4672],{"alt":4673,"src":4674},"React Brussels","https://res.cloudinary.com/debsobrien/image/upload/v1673856494/debbie.codes/blog/2022/react-brussels_esunrb.jpg",[11,4676,4677],{},"October was also Vite conf where I had pre-recorded my talk on Playwright and how Playwright uses Vite for component testing. This conference was on discord and had a huge amount of viewers.",[11,4679,4680],{},"I got to fly home to Mallorca for a few days to then pack again and fly to Nantes in France to give my talk on Playwright to a massive audience and also work on the Microsoft booth. This is such a fun conference with most talks in French. They put a lot of work into theming the booths which makes it so much fun.",[11,4682,4683],{},[121,4684],{"alt":4685,"src":4686},"dev fest nantes Microsoft booth","https://res.cloudinary.com/debsobrien/image/upload/v1673856525/debbie.codes/blog/2022/devfest-france_avnfzf.jpg",[23,4688,4690],{"id":4689},"november-online-conferences-and-podcasts","November: Online conferences and podcasts",[11,4692,4693],{},"November was a lot quieter with less traveling. I gave my talk for Build Stuff remotely. As much as I wanted to be there in person it just wasn't possible. But it was nice to see people come to my talk even though I was not there in person. Giving remote talks is not the same though and you really do miss out on the networking and hallway chats.",[11,4695,4696],{},"It was nice to have a break from giving talks though and I got to have fun doing podcast interviews.",[23,4698,4700],{"id":4699},"december-holidays-and-family-time-and-podcast-interviews","December: Holidays and family time and podcast interviews",[11,4702,4703],{},"December was a great month. I had holidays to take so we booked a cruise on the Red Sea. It was amazing and I totally recommend. We first flew to Madrid and then Egypt. First stop on the cruise was Petra in Jordan which is a place I have always wanted to visit and it did not disappoint. In fact it was so much better than I had imagined. So much bigger and impressively well maintained. Incredible.",[11,4705,4706],{},"Then we went to Saudi Arabia which was so nice to see and meet the local people there especially as Saudi only opened to tourism in 2019 so it's all very new to lots of people there and they are so welcoming and friendly.",[11,4708,4709,4710],{},"Then the Pyramids which are out of this world and I recommend everyone should visit. Just so big, still there after all these years and the fact that they even considered building these just to bury someone is actually just incredible.\n",[121,4711],{"alt":4712,"src":4713},"pyramids Egypt","https://res.cloudinary.com/debsobrien/image/upload/v1673861294/debbie.codes/blog/2022/pyramids_fzo4p9.jpg",[11,4715,4716],{},"I would have loved to go for runs in all these countries but it just wasn't possible. I did manage to run on the cruise ship though which was still nice even though it was just running in a straight line and back again and again.",[11,4718,4719],{},"More podcast interviews in December on Playwright which was a lot of fun.",[11,4721,4722],{},"And then Christmas holidays back in Ireland with more family time, switch off from computer time and running in the countryside making for a great way to end the year. The day after Christmas I ran my second race ever. A family 5k race where I got to run with my brother and nieces in Wicklow town. It was a great experience.",[11,4724,4725],{},[121,4726],{"alt":4727,"src":4728},"race in ireland","https://res.cloudinary.com/debsobrien/image/upload/v1673861438/debbie.codes/blog/2022/race-ireland_amyqrb.jpg",[23,4730,3672],{"id":3671},[11,4732,4733],{},"After writing all this down I think to myself. Wow, what a year. If I didn't write it all down I might actually not consider the year so successful as it is so easy to forget what you have done and achieved. This is why it is so important to look back and reflect on the year.",[11,4735,4736,4737,4741],{},"So things that stand out are the fact that I achieved a massive goal of mine, ",[15,4738,4740],{"href":4739},"/blog/why-microsoft","to work at Microsoft which had been a 5 year plan"," I had been working on since 4 years ago. I still can't believe I work for Microsoft.",[11,4743,4744],{},"Last year in my end of year review I talked about not accepting the defaults and I very much did live on the edge in 2022. I feel it's something that should be mentioned every year and should be something to live by.",[1713,4746,4747],{},[11,4748,4749,4752],{},[15,4750,4318],{"href":4316,"rel":4751},[19]," by Abel Wang.",[11,4754,4321],{},[11,4756,4757],{},"A few years ago I wrote in my journal that I wanted to travel more and do more sport and I certainly achieved both of them in 2022",[11,4759,4760],{},"Although October was pretty intense conference wise and probably too much, overall I did manage to avoid burnout. I think this is because I set my sport goal to 3 hours a day which meant I didn't have time to do 12 hour coding days which is just not good for you at all. Sport was my key to avoiding burnout and sport makes me happy and we should all do more of what makes us happy.",[23,4762,3682],{"id":3681},[11,4764,4765],{},"So when you achieve your dreams what goals do you set yourself? I have always been goal driven so there is still so much more to achieve. I want to make sure I am still in Microsoft by the end of the year and not just achieving lots and learning lots in work but also continuing to be happy in my job and with my team.",[11,4767,4768],{},"Imposter syndrome still holds me back and I am still trying to break those barriers down but hopefully I will manage to just do the stuff I want to do without worrying about failure or being wrong. I set myself such high standards that are impossible to live up to and I need to be easier on myself and just do stuff without so much thought process.",[11,4770,4771],{},"My big goal this year is to be well known in the testing space with deep technical knowledge in this area. This will require lots of in dept studying on not just Playwright but testing in general and from there I should be able to create great content and help others.",[11,4773,4774],{},"Goals include becoming a keynote speaker, getting more video content out there, improving my coding knowledge and not relying on others to help me but being able to dive deeper into the code so my brain can figure things out on it's own. Often times I believe I can't do something and reach out for help instead of spending more time trying to figure it out. Patience and self belief.",[11,4776,4777],{},"When it comes to sport I want to continue doing lots of sport but this year the goal is to do less but yet do more. I want to improve my timings both in cycling and running. Run faster, cycle faster. My pace is not good due to issues I have with my diaphragm but I seem to be getting over it and can push myself more so this year it's all about pushing boundaries. Races, marathon, tournaments, gran fondos.",[11,4779,4780],{},"Sometimes the only way to improve is to change something. That might mean changing instructors or clubs. Finding a running club or community or signing up for races so I have a challenge and a time to work towards. Perhaps using the car to drive to places so I can run in different areas. Cycling more so I can improve and do more gran fondo challenges. A new bike is on the cards and should help me improve a lot as my bike is over 10 years old now.",[11,4782,4783],{},"This year we are going to house hunt. I have been renting this apartment for 11 years and although I love it, it isn't mine. Meaning I can't do anything to improve it or change it. Now that I have a good and secure job and have all debts paid off I can start saving for a deposit and apply for a mortgage. The hardest part of course will be finding somewhere that we can afford. But luckily we are not in a hurry and therefore it's more like a year or two year long project with no stress as we can still stay living where we are for a few years more if needs be.",[11,4785,4786],{},"Overall the goal this year is to go above and beyond to achieve the unachievable. If I think I can't achieve it then I need to aim for it, train/study/prepare for it, put time into it and then give it my all and surprise myself at where it will take me. Work hard, preparation, let go and just believe.",[11,4788,4789],{},"Happy 2023 everyone. Let's make it an amazing one.",{"title":307,"searchDepth":748,"depth":748,"links":4791},[4792,4793,4794,4795,4796,4797,4798,4799,4800,4801,4802,4803,4804,4805,4806],{"id":3477,"depth":748,"text":3478},{"id":4400,"depth":748,"text":4401},{"id":4437,"depth":748,"text":4438},{"id":4453,"depth":748,"text":4454},{"id":4475,"depth":748,"text":4476},{"id":4518,"depth":748,"text":4519},{"id":4546,"depth":748,"text":4547},{"id":4574,"depth":748,"text":4575},{"id":4593,"depth":748,"text":4594},{"id":4606,"depth":748,"text":4607},{"id":4637,"depth":748,"text":4638},{"id":4689,"depth":748,"text":4690},{"id":4699,"depth":748,"text":4700},{"id":3671,"depth":748,"text":3672},{"id":3681,"depth":748,"text":3682},"2022-12-31","And there it is, another year gone but when you look back at all you have achieved then really there is no more to say than; what a year. From Google interviews to being hired by Microsoft, speaking at conferences and traveling to so many conferences and lots of sport, 2022 was a great year.","v1673861872/debbie.codes/blog/2022/microsoft-running_gztjdu.jpg",{"ogImage":4811},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651559990/debbie.codes/blog/2022/microsoft-running_gztjdu.jpg","/blog/2022-in-review",{"title":4381,"description":4808},"blog/2022-in-review",[3464],"N2h4Xiiqf8ii-4gZXTTUa9qTrQXWDIHOgYt3uQeNSFU",{"id":4818,"title":4819,"body":4820,"canonical":788,"date":5213,"description":5214,"extension":786,"featured":787,"image":5215,"meta":5216,"navigation":790,"ogimage":788,"path":5217,"provider":3460,"published":790,"seo":5218,"stem":5219,"tags":5220,"url":788,"__hash__":5222},"blog/blog/adding-tailwind-to-bit.md","Adding TailwindCSS to Bit",{"type":8,"value":4821,"toc":5210},[4822,4825,4831,4834,4837,4844,4848,4855,4885,4892,4952,4961,4977,4987,5006,5009,5078,5085,5138,5149,5161,5164,5167,5181,5185,5188,5191,5207],[11,4823,4824],{},"When using Bit we are working with Components in isolation. The consuming app is what would normally have TailwindCSS installed and therefore just adding the classes to the component will work when consuming the component in an app that has Tailwind installed. However when working with Bit we work in isolation meaning we build a component separate to the app. The component is like a mini app and therefore we can see what it looks like with the Bit dev server.",[11,4826,4827],{},[121,4828],{"alt":4829,"src":4830},"Bit and Tailwind","https://res.cloudinary.com/debsobrien/image/upload/v1640961761/debbie.codes/blog/tailwindCSS-bit_eimfbh.png",[11,4832,4833],{},"We can then build our component, build different compositions of what our component would look like with different states for example. We can also write tests for our component as well as documentation to make it easier for others to consume our components. Our components can then be easily installed and used in any of our apps or in other components.",[11,4835,4836],{},"In order to add tailwind to a component we need to apply an environment to the component. By doing this we can ensure not just one but all components that have this environment applied can have the save things applied such as linting, framework and in our case tailwind.",[11,4838,4839,4840,4843],{},"When building a new project with the ",[179,4841,4842],{},"bit new"," command an env component will be provided for you that you can then modify to how you like.",[23,4845,4847],{"id":4846},"adding-tailwind-to-your-env","Adding Tailwind to your Env",[11,4849,4850,4851,4854],{},"In our custom env component add a tailwind folder with a ",[179,4852,4853],{},"styles.css"," file and add in the following",[299,4856,4860],{"className":4857,"code":4858,"language":4859,"meta":307,"style":307},"language-css shiki shiki-themes github-light github-dark","@tailwind base;\n@tailwind components;\n@tailwind utilities;\n","css",[179,4861,4862,4871,4878],{"__ignoreMap":307},[1736,4863,4864,4868],{"class":1738,"line":1739},[1736,4865,4867],{"class":4866},"szBVR","@tailwind",[1736,4869,4870],{"class":1912}," base;\n",[1736,4872,4873,4875],{"class":1738,"line":748},[1736,4874,4867],{"class":4866},[1736,4876,4877],{"class":1912}," components;\n",[1736,4879,4880,4882],{"class":1738,"line":756},[1736,4881,4867],{"class":4866},[1736,4883,4884],{"class":1912}," utilities;\n",[11,4886,4887,4888,4891],{},"Then add a ",[179,4889,4890],{},"tailwind.config.js"," file",[299,4893,4897],{"className":4894,"code":4895,"language":4896,"meta":307,"style":307},"language-jsx shiki shiki-themes github-light github-dark","module.exports = {\n  theme: {\n    extend: {}\n  },\n  variants: {\n    extend: {}\n  },\n  plugins: []\n}\n","jsx",[179,4898,4899,4915,4920,4925,4930,4935,4939,4943,4948],{"__ignoreMap":307},[1736,4900,4901,4904,4906,4909,4912],{"class":1738,"line":1739},[1736,4902,4903],{"class":1918},"module",[1736,4905,891],{"class":1912},[1736,4907,4908],{"class":1918},"exports",[1736,4910,4911],{"class":4866}," =",[1736,4913,4914],{"class":1912}," {\n",[1736,4916,4917],{"class":1738,"line":748},[1736,4918,4919],{"class":1912},"  theme: {\n",[1736,4921,4922],{"class":1738,"line":756},[1736,4923,4924],{"class":1912},"    extend: {}\n",[1736,4926,4927],{"class":1738,"line":1755},[1736,4928,4929],{"class":1912},"  },\n",[1736,4931,4932],{"class":1738,"line":1761},[1736,4933,4934],{"class":1912},"  variants: {\n",[1736,4936,4937],{"class":1738,"line":1767},[1736,4938,4924],{"class":1912},[1736,4940,4941],{"class":1738,"line":1772},[1736,4942,4929],{"class":1912},[1736,4944,4945],{"class":1738,"line":1778},[1736,4946,4947],{"class":1912},"  plugins: []\n",[1736,4949,4950],{"class":1738,"line":1784},[1736,4951,1976],{"class":1912},[11,4953,4954,4955,4960],{},"Next we need to install the ",[15,4956,4959],{"href":4957,"rel":4958},"https://bit.dev/bit-foundations/styling/tailwind/webpack-transformer",[19],"TailwindCSS webpack transformer component from Bit"," and once installed we can then add it to our custom env component.",[299,4962,4964],{"className":2665,"code":4963,"language":2667,"meta":307,"style":307},"bit install @bit-foundations/styling.tailwind.webpack-transformer\n",[179,4965,4966],{"__ignoreMap":307},[1736,4967,4968,4971,4974],{"class":1738,"line":1739},[1736,4969,4970],{"class":2674},"bit",[1736,4972,4973],{"class":1935}," install",[1736,4975,4976],{"class":1935}," @bit-foundations/styling.tailwind.webpack-transformer\n",[11,4978,4979,4980,4891],{},"In your environment component add the following to the ",[15,4981,4984],{"href":4982,"rel":4983},"https://bit.dev/learn-bit-react/base-ui/env/learn-bit-react/~code/learn-bit-react.main.runtime.ts",[19],[179,4985,4986],{},"main.runtime.ts",[299,4988,4990],{"className":4894,"code":4989,"language":4896,"meta":307,"style":307},"import { UseTailwindTransformer } from '@bit-foundations/styling.tailwind.webpack-transformer'\n",[179,4991,4992],{"__ignoreMap":307},[1736,4993,4994,4997,5000,5003],{"class":1738,"line":1739},[1736,4995,4996],{"class":4866},"import",[1736,4998,4999],{"class":1912}," { UseTailwindTransformer } ",[1736,5001,5002],{"class":4866},"from",[1736,5004,5005],{"class":1935}," '@bit-foundations/styling.tailwind.webpack-transformer'\n",[11,5007,5008],{},"Then inside the provider add the following code",[299,5010,5012],{"className":4894,"code":5011,"language":4896,"meta":307,"style":307},"static async provider([react, envs]: [ReactMain, EnvsMain]) {\nconst {\n      previewConfigTransformer: twPreviewTransformer,\n      devServerConfigTransformer: twDevServerTransformer\n    } = UseTailwindTransformer(tailwindConfigPath);\n...\n}\n",[179,5013,5014,5025,5032,5045,5055,5069,5074],{"__ignoreMap":307},[1736,5015,5016,5019,5022],{"class":1738,"line":1739},[1736,5017,5018],{"class":1912},"static async ",[1736,5020,5021],{"class":2674},"provider",[1736,5023,5024],{"class":1912},"([react, envs]: [ReactMain, EnvsMain]) {\n",[1736,5026,5027,5030],{"class":1738,"line":748},[1736,5028,5029],{"class":4866},"const",[1736,5031,4914],{"class":1912},[1736,5033,5034,5038,5040,5043],{"class":1738,"line":756},[1736,5035,5037],{"class":5036},"s4XuR","      previewConfigTransformer",[1736,5039,3065],{"class":1912},[1736,5041,5042],{"class":1918},"twPreviewTransformer",[1736,5044,1939],{"class":1912},[1736,5046,5047,5050,5052],{"class":1738,"line":1755},[1736,5048,5049],{"class":5036},"      devServerConfigTransformer",[1736,5051,3065],{"class":1912},[1736,5053,5054],{"class":1918},"twDevServerTransformer\n",[1736,5056,5057,5060,5063,5066],{"class":1738,"line":1761},[1736,5058,5059],{"class":1912},"    } ",[1736,5061,5062],{"class":4866},"=",[1736,5064,5065],{"class":2674}," UseTailwindTransformer",[1736,5067,5068],{"class":1912},"(tailwindConfigPath);\n",[1736,5070,5071],{"class":1738,"line":1767},[1736,5072,5073],{"class":4866},"...\n",[1736,5075,5076],{"class":1738,"line":1772},[1736,5077,1976],{"class":1912},[11,5079,5080,5081,5084],{},"Inside your ",[179,5082,5083],{},"envs.compose"," where you override files for TypScript, webpack, Jest etc add in the following code to webpack so you can use the TailwindPreview and DevServer Transformers",[299,5086,5088],{"className":4894,"code":5087,"language":4896,"meta":307,"style":307},"const MyEnvEnv = envs.compose(react.reactEnv, [\n\n      react.useWebpack({\n        previewConfig: [twPreviewTransformer],\n        devServerConfig: [twDevServerTransformer]\n      }),\n",[179,5089,5090,5108,5112,5123,5128,5133],{"__ignoreMap":307},[1736,5091,5092,5094,5097,5099,5102,5105],{"class":1738,"line":1739},[1736,5093,5029],{"class":4866},[1736,5095,5096],{"class":1918}," MyEnvEnv",[1736,5098,4911],{"class":4866},[1736,5100,5101],{"class":1912}," envs.",[1736,5103,5104],{"class":2674},"compose",[1736,5106,5107],{"class":1912},"(react.reactEnv, [\n",[1736,5109,5110],{"class":1738,"line":748},[1736,5111,1747],{"emptyLinePlaceholder":790},[1736,5113,5114,5117,5120],{"class":1738,"line":756},[1736,5115,5116],{"class":1912},"      react.",[1736,5118,5119],{"class":2674},"useWebpack",[1736,5121,5122],{"class":1912},"({\n",[1736,5124,5125],{"class":1738,"line":1755},[1736,5126,5127],{"class":1912},"        previewConfig: [twPreviewTransformer],\n",[1736,5129,5130],{"class":1738,"line":1761},[1736,5131,5132],{"class":1912},"        devServerConfig: [twDevServerTransformer]\n",[1736,5134,5135],{"class":1738,"line":1767},[1736,5136,5137],{"class":1912},"      }),\n",[11,5139,5140,5141,5148],{},"Next in the ",[15,5142,5145],{"href":5143,"rel":5144},"https://bit.dev/learn-bit-react/base-ui/env/learn-bit-react/~code/learn-bit-react.preview.runtime.ts",[19],[179,5146,5147],{},"preview.runtime.ts"," file of your custom env import the css file we created earlier.",[299,5150,5152],{"className":4894,"code":5151,"language":4896,"meta":307,"style":307},"import './tailwind/styles.css'\n",[179,5153,5154],{"__ignoreMap":307},[1736,5155,5156,5158],{"class":1738,"line":1739},[1736,5157,4996],{"class":4866},[1736,5159,5160],{"class":1935}," './tailwind/styles.css'\n",[11,5162,5163],{},"And that's it. You can now use Tailwind classes in your component and see them in your component compositions.",[11,5165,5166],{},"Note: Make sure postcss has been installed and is in your workspace.json. If not just install it with bit.",[299,5168,5170],{"className":2665,"code":5169,"language":2667,"meta":307,"style":307},"bit install postcss\n",[179,5171,5172],{"__ignoreMap":307},[1736,5173,5174,5176,5178],{"class":1738,"line":1739},[1736,5175,4970],{"class":2674},[1736,5177,4973],{"class":1935},[1736,5179,5180],{"class":1935}," postcss\n",[11,5182,5183],{},[121,5184],{"alt":4829,"src":4830},[11,5186,5187],{},"Note that this only works for Tailwind version 2. Due to the JIT compile used in Tailwind 3 more work needs to be done to be able to get this working in Bit so for now only Tailwind version 2 is supported.",[11,5189,5190],{},"Useful links:",[70,5192,5193,5200],{},[73,5194,5195],{},[15,5196,5199],{"href":5197,"rel":5198},"https://bit.dev/learn-bit-react/base-ui/ui/button",[19],"button component that uses TailwindCSS",[73,5201,5202],{},[15,5203,5206],{"href":5204,"rel":5205},"https://bit.dev/learn-bit-react/base-ui/env/learn-bit-react",[19],"custom env component that adds TailwindCSS",[2011,5208,5209],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":307,"searchDepth":748,"depth":748,"links":5211},[5212],{"id":4846,"depth":748,"text":4847},"2021-12-30","Adding TailwindCSS to your Bit components by adding it to the env that is used by your components. This way the the component compositions will show the TailwindCSS classes.","v1640962583/debbie.codes/blog/tailwindcss-button_2x_gtcqu9",{},"/blog/adding-tailwind-to-bit",{"title":4819,"description":5214},"blog/adding-tailwind-to-bit",[5221],"react","NEt2oCkd5VPRcUpc8PrabHFTAgozA2_uILXPqphdG5A",{"id":5224,"title":5225,"body":5226,"canonical":788,"date":5230,"description":5231,"extension":786,"featured":787,"image":5232,"meta":5233,"navigation":790,"ogimage":788,"path":5234,"provider":5235,"published":787,"seo":5236,"stem":5237,"tags":5238,"url":5241,"__hash__":5242},"blog/blog/advanced-i18n-in-nuxt-using-interpolations.md","Advanced i18n in Nuxt using Interpolations",{"type":8,"value":5227,"toc":5228},[],{"title":307,"searchDepth":748,"depth":748,"links":5229},[],"2020-09-17","That's where i18n interpolation comes into practice. With i18n, instead of using v-html we can instead use interpolation. How? i18n gives us an \u003Ci18n> component that we can use on any of our pages or inside our components.","photo-1600633349333-eebb43d01e23?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2550&q=80",{"loading":3458,"platform":4232},"/blog/advanced-i18n-in-nuxt-using-interpolations","imgix",{"title":5225,"description":5231},"blog/advanced-i18n-in-nuxt-using-interpolations",[5239,5240],"nuxt","vue","https://vuedose.tips/advanced-i18n-in-nuxt-using-interpolations/","FUwPxmjay1R_NUDKUA9qIqoLs235GXCMImQ9aqWjh20",{"id":3248,"title":3249,"body":5244,"canonical":3313,"date":3314,"description":3315,"extension":786,"featured":787,"image":788,"meta":5286,"navigation":790,"ogimage":788,"path":3317,"provider":788,"published":790,"seo":5287,"stem":3319,"tags":5288,"url":788,"__hash__":3322},{"type":8,"value":5245,"toc":5280},[5246,5248,5250,5252,5254,5256,5258,5260,5262,5264,5266,5268,5270,5272,5274,5276,5278],[11,5247,3254],{},[23,5249,3258],{"id":3257},[11,5251,3261],{},[23,5253,3265],{"id":3264},[11,5255,3268],{},[11,5257,3271],{},[23,5259,3275],{"id":3274},[11,5261,3278],{},[11,5263,3281],{},[11,5265,3284],{},[11,5267,3287],{},[11,5269,3290],{},[23,5271,3294],{"id":3293},[11,5273,3297],{},[11,5275,3300],{},[11,5277,3303],{},[11,5279,3306],{},{"title":307,"searchDepth":748,"depth":748,"links":5281},[5282,5283,5284,5285],{"id":3257,"depth":748,"text":3258},{"id":3264,"depth":748,"text":3265},{"id":3274,"depth":748,"text":3275},{"id":3293,"depth":748,"text":3294},{},{"title":3249,"description":3315},[3321,795],{"id":5290,"title":5291,"body":5292,"canonical":788,"date":5487,"description":5488,"extension":786,"featured":787,"image":5489,"meta":5490,"navigation":790,"ogimage":788,"path":5491,"provider":3460,"published":787,"seo":5492,"stem":5493,"tags":5494,"url":788,"__hash__":5496},"blog/blog/amazing-things-are-going-to-happen.md","Amazing Things are Going to Happen",{"type":8,"value":5293,"toc":5472},[5294,5299,5309,5312,5315,5318,5322,5325,5328,5331,5334,5338,5341,5344,5348,5351,5354,5357,5361,5364,5367,5370,5374,5377,5380,5384,5387,5390,5393,5396,5400,5403,5406,5410,5413,5416,5420,5423,5426,5430,5433,5436,5440,5443,5446,5450,5453,5456,5460,5463,5466],[11,5295,5296],{},[133,5297,5298],{},"Listen to the Audio:",[5300,5301,5302,5303,5308],"audio",{"controls":790},"\n  ",[5304,5305],"source",{"src":5306,"type":5307},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1619364201/debbie.codes/blog/debbie_ewgkaa.mp3","audio/mp3","\nYour browser does not support the audio element.\n",[11,5310,5311],{},"I am feeling absolutely amazing and I just wanted to share it all with you. It's kind of interesting in how it happened, perhaps just a process of events, but let me explain. I guess I could kind of say it's similar to when parents say their kids wake up in the morning and they see they've grown. And they say, Oh my God you've just grown. You've had a growth sprout and that's how I feel right now. Like I've grown and you're kind of going what? No, because my height is exactly the same, but I just feel I have grown right now in a way that I can't even explain. How did it happen?",[11,5313,5314],{},"Literally I went for a cycle in the mountains and it all just happened. Okay. Let me go back a little bit. No, seriously. I was cycling in the mountains. And when I'm cycling in the mountains, it's like you have a lot of time to just think and just process things. Your mind is clear and you get a lot of things out of you.",[11,5316,5317],{},"And today I just got it out of me, all the negativity that's been holding me back and stopping me from being me, all the voices that are in my head that have been stopping me from believing in myself.",[23,5319,5321],{"id":5320},"not-being-accepted","Not being Accepted",[11,5323,5324],{},"Before I joined Nuxt I was working at some companies where I just didn't fit in. I wasn't accepted by people. Not just did they not believe in me. People didn't like me. People didn't want to be around me. They didn't want to work with me. Maybe it was all in my head, but it wasn't. I wished it was. But it wasn't, it was really hard. It was such a hard environment to work in. But when you work so hard to get a job in tech and you get a job, you can't just leave and go somewhere else.",[11,5326,5327],{},"It's not that easy. So now you have this great job. You have this great career, but you're working in an environment where people don't want you around. Why? I don't know. Sometimes I guess I'll never know. I was the type of person that was pushing things forward. I was making change. I was bringing change into the work environment, into technology. Not everyone is ready for change, some people just didn't want to change.",[11,5329,5330],{},"I also probably wasn't, the best role model because I was leading a team and I wanted people to be like me, I didn't understand why people didn't want to be like me, why they didn't want to go home after work and open their computer and start watching a conference talk or go to a meetup or study or do a course or spend two hours learning something new.",[11,5332,5333],{},"I mean, why would you not want to do that? Why would you want to go home and watch Netflix, for example? I was trying so hard to be better that I just kind of thought everyone else was like me and doing the same. And I probably pushed that on a lot of people. And that was probably hard for people to work with me.",[23,5335,5337],{"id":5336},"being-successful-can-be-hard","Being Successful can be hard",[11,5339,5340],{},"Some people just didn't like my success. And that was really hard as well. I became Google Developer Expert, which was just an incredible achievement for me, but it was not acknowledged at work. When I got my Microsoft Most Valuable Professional award which was sent to the office. That's where you're spending most of your time so the award arrived at the office. And I took it out of the box and this is an absolutely beautiful award and a plaque. It was just incredible. And I put it on my desk and I was so proud that I'd achieved this.",[11,5342,5343],{},"But people laughed at it. People laughed at me. I seen them from the other side of the room so I brought it to my boss's attention. I told him, and he just told me that we don't celebrate success here. You should just take it home. You're just showing off and making people feel like that they're not good enough. And you're not better than anyone else. That was so hard. Instead of the company taking a picture of you with your award and saying, well done and promoting the fact that one of their employees had just become an MVP, a Microsoft most valuable professional, and using that to help their company grow. Instead of that, I just got told to take it home. It always stayed on my shelf in my room as kind of like something that I was proud of, but afraid to be proud of, afraid to feel that it was worth something good. And every day I went to work, I just didn't want to be there.",[23,5345,5347],{"id":5346},"going-to-conferences-was-my-way-of-escaping","Going to Conferences was my way of Escaping",[11,5349,5350],{},"That's when I started going to a lot of conferences. I just wanted to go to conferences all the time. Because when I was at conferences people accepted me. People told me I was amazing. I was surrounded by these people who just had positivity and then I'd go into the office and it was all negative. It was all fights. It was battles.",[11,5352,5353],{},"I went to meetings where I would talk and people would just talk over me. In one company, I went to a meeting where I stood up to give a presentation and they all just started talking and arguing and shouting over each other. And I stood there with my arms folded thinking, I could walk out of this room right now, go and get a coffee and come back and they would not have even noticed that I had left the room. And this was my experience of giving a presentation. It was just horrible. It was so horrible. No matter what I did, people just did not accept me.",[11,5355,5356],{},"I went home from work in tears on many occasions, many occasions I wanted to give up. I wanted to walk away. I didn't want to be there. I had a really, really good CEO at a previous company who told me. \"Don't ever give up, never, ever give up, no matter how hard it gets, people will always try and bring you down, be strong and just keep going.\" And so I did and I kept going.",[23,5358,5360],{"id":5359},"it-was-destroying-my-mental-health","It was Destroying my Mental Health",[11,5362,5363],{},"I went to a conference one day and when I came back, at the time I was a tech lead, they had basically moved all of the desks and chairs around and rearranged themselves, and they'd left me on the other side of the room, alone. I am meant to lead this team, but, I am not allowed sit near them or hear what's going on. And they told me, \"you're a manager you can't sit with us\" and I told them, \"I'm a developer first, I lead the developers\".",[11,5365,5366],{},"It was really, really hard to deal with. I kept analyzing things in my head. I kept going over everything. What am I doing wrong? Why is this happening to me? Why do people not like me? I'm not a bad person. I'm just doing my job. And I'm trying to do my job really, really well. And for some reason, people don't want me around and I didn't know what to do.",[11,5368,5369],{},"It got so bad that I went on a skiing holiday and I couldn't even ski down the mountain because all this was in my head. All this was just going round and round, like a record playing, and I was analyzing and analyzing it. I just stopped on the mountain and I was in tears. I couldn't ski. It had taken over my life in such a way that it was so hard to deal with.",[23,5371,5373],{"id":5372},"speaker-friends-and-counselling","Speaker Friends and Counselling",[11,5375,5376],{},"I was really lucky to have had such amazing people, amazing speaker friends. One of them, gave me a counselling session, a few counselling sessions actually, and talking it over with her really helped and made me feel that it wasn't me. It's not on me. It's not my fault. I'm not doing anything wrong.",[11,5378,5379],{},"Basically I'm trying to fit in somewhere where I just don't belong. And that all these other people out there in the world think I'm amazing. Why should I focus my attention on the handful of people that don't? Focus on the people that actually want to read my blog posts, want to listen to me at a conference, want to watch my YouTube videos, concentrate on those people, because they're the ones that you need to focus your energy towards, and forget the others. Now that's such an easy thing to say, it really is, especially when you walk into the office, into a place where they're looking at you every day.",[23,5381,5383],{"id":5382},"the-power-of-thank-you","The Power of Thank you",[11,5385,5386],{},"And there was something really interesting she told me. She said,",[11,5388,5389],{},"\"Instead of fighting back, you just have to stop fighting because anger with anger, hatred with hatred, no one wins. You're going to have to start doing things different and it's going to be really hard, but you can do it. You have got to use the word 'Thank You'. And the next day you go into work and they start attacking you for something or telling you you're not doing your job properly or you're wrong, or this is not good enough, you just have to answer with something like: Thank you, I'll keep that in mind. I'll improve that. And once you use the word, thank you. There's no way to fight back.\"",[11,5391,5392],{},"And when I used it, they just stood in their shoes and they were happy. And they said okay. And they walked off. I mean, obviously it wasn't a great solution. How can you work like that? Teamwork doesn't exist. But it got me through the day. It stopped the fights. It stopped the battles. And it allowed me to concentrate on moving forward, on getting out of that environment and finding another job, finding a place where I was going to be accepted and wanted, where people believed in me and where people were excited for me growing and being successful and helped me to grow and be better.",[11,5394,5395],{},"So after a couple of weeks of dealing with this and just saying, thank you. As people put me down every day, I would just say, thank you. I didn't know that, I'll improve on that. And I got on about my job.",[23,5397,5399],{"id":5398},"i-just-tried-to-not-go-to-the-office","I just tried to not go to the Office",[11,5401,5402],{},"I was really lucky that I had really good clients and I was able to focus my energy on these clients who really valued me and who wanted me around. So I just kind of constantly worked with the external clients who are brilliant and I tried to not go to the office as much as I could. I Tried to work remote, tried to go to conferences and just work from there, because that was where I was happy. And being in work, being in the office, going to work, was just not a good place.",[11,5404,5405],{},"I was really lucky that after a couple of months, it took a couple of months to get there. And finally I was offered a job at Nuxt. That's when I was able to move on. But just because you change companies doesn't mean that you get rid of all that negativity that's been there for years just being thrown into you, you hold onto it for some reason, and it's not so easy to just let it go.",[23,5407,5409],{"id":5408},"i-just-didnt-believe-in-myself","I just didn't Believe in myself",[11,5411,5412],{},"How do you just let things go? I didn't really realize that it was still there, that all these emotions, all these voices were still there. All I knew was that I didn't believe in myself and even in my new job in Bit, when I started there after the first week, I told them \"you've hired the wrong person. I'm not good enough for this job.\" I told them many times I'm technically not good enough. Now I'm a Google Developer Expert, a Microsoft MVP, a GitHub star, a Media Developer Expert, and I'm telling people I'm not good enough. Like that's crazy.",[11,5414,5415],{},"But I didn't believe it because I had years and years of people telling me that I'm not good enough, years of people not believing in me, years of people putting me down, years of people not wanting me around, not accepting me. And for some reason, I seemed to care about them more than I cared about the rest of the world. Why? I don't know.",[23,5417,5419],{"id":5418},"joining-bit-has-been-the-best-thing-ever","Joining Bit has been the best thing ever",[11,5421,5422],{},"I think moving to Bit has really, really helped me. I've been surrounded by people who are just telling me I'm doing amazing, who are pushing me forward. I'm seeing myself grow.",[11,5424,5425],{},"I'm seeing myself doing more programming, getting better and I feel more comfortable. I feel stronger and the team are helping me and pushing me towards this direction. I'm working with people who are telling me that they think I'm a star. It's like, they want to work with me. And they're proud to have me on board and have me working there. And it's just such a great feeling, it's incredible.",[23,5427,5429],{"id":5428},"receiving-the-github-star-award","Receiving the GitHub Star Award",[11,5431,5432],{},"Then when GitHub sent me the GitHub Star Award, and I have this physical award that I'm looking at thinking, wow. Like this is incredible. When I got awarded the Google Developer Expert award, I actually believed they'd sent it to the wrong person, or that they just couldn't find someone else to award it to. Seriously, I know that's crazy, but that is exactly how I felt. And even with the GitHub Star, I was like, why would people nominate me? Why do people think I'm good? Why do people think I'm a star? I'm not a star. I'm not good enough because in my head, that's all I was hearing.",[11,5434,5435],{},"But when I got this award and I have it here in the place where I'm working every day, I see it every day. And then I get a GitHub Star t-shirt and a hoodie and I'm wearing them and I'm looking down and it's right there, the star. And I'm like, actually, you know what, maybe I am a star. Maybe I am actually good at what I do.",[23,5437,5439],{"id":5438},"maybe-im-actually-really-good-at-what-i-do","Maybe I'm actually really good at what I do",[11,5441,5442],{},"And as I went for that cycle today in the mountains, as I'm cycling along, all these memories came back up, all these thoughts, but they all just came up and just released and all of a sudden, and I seriously don't know how, but all of a sudden they became not important. Those voices were not holding me down anymore. They're not stopping me. They don't mean anything to me anymore. And that's how I'm able to talk about it today. Whereas before, there's no way I would have talked about it. And now it's like, you know what? Maybe just, maybe I'm actually really good at what I do. Not",[11,5444,5445],{},"I feel now I can just grow and be better and actually just move forward instead of constantly thinking I'm not good enough or I'm not learning fast enough or I'm not able to do this. People are wrong in believing in me. I'm not good enough because actually, you know what? Just because someone tells you you're not good enough, that does not mean that, just because some people tell you you don't belong here. That does not mean you don't belong. You're just in the wrong place and right now, I'm so in the right place. I belong. I am good enough. And yeah, I am a star and I believe right now, I don't know. I really, really believe I can do anything right now.",[23,5447,5449],{"id":5448},"theres-no-one-stopping-me-anymore","There's no one stopping me anymore",[11,5451,5452],{},"It's really weird. I can't even explain it properly, but I just feel. I can do anything. There's no one holding me back anymore. There's no one stopping me. There's no one like dragging me from behind saying, you can't do this. I can just do it. And I guess I just wanted to share this story to maybe help others see that it is possible.",[11,5454,5455],{},"If you're feeling that way, that you can't do it, that someone's telling you that you can't then don't listen to the negativity. Don't listen to the people who put you down. Be strong, surround yourself with the people who believe in you and listen to them and stop listening to the others.",[23,5457,5459],{"id":5458},"lets-build-a-better-world","Let's build a better world",[11,5461,5462],{},"It's taken me a year and a few months to get here. So it's not an easy process, but I'm here. I've made it and you can too. And now. I don't know. I just want to help others in getting there. And I'm just going to do really cool and amazing things. Just watch. Amazing things are going to happen.",[11,5464,5465],{},"I don't even know what, I just know it and I'm just really excited and I'm just like, yeah, let's do this together. Let's build a better world. So people don't have to go through what I went through. Let's do it together.",[11,5467,5468],{},[121,5469],{"alt":5470,"src":5471},"my awards","https://res.cloudinary.com/debsobrien/image/upload/f_auto/v1619366231/debbie.codes/blog/IMG_8224_pe76ma.webp",{"title":307,"searchDepth":748,"depth":748,"links":5473},[5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486],{"id":5320,"depth":748,"text":5321},{"id":5336,"depth":748,"text":5337},{"id":5346,"depth":748,"text":5347},{"id":5359,"depth":748,"text":5360},{"id":5372,"depth":748,"text":5373},{"id":5382,"depth":748,"text":5383},{"id":5398,"depth":748,"text":5399},{"id":5408,"depth":748,"text":5409},{"id":5418,"depth":748,"text":5419},{"id":5428,"depth":748,"text":5429},{"id":5438,"depth":748,"text":5439},{"id":5448,"depth":748,"text":5449},{"id":5458,"depth":748,"text":5459},"2021-04-26","Believing in yourself is hard, especially when for so long people have put you down or not believed in you. Getting those voices out of your head is not easy but I think I have finally done it and now I can finally grow and just be a star.","v1619366231/debbie.codes/blog/IMG_8227_bav9rh",{"loading":3458},"/blog/amazing-things-are-going-to-happen",{"title":5291,"description":5488},"blog/amazing-things-are-going-to-happen",[3464,5495],"motivation","aTl0p0o5M3zvpc-Ic3_JGmFpyrf2_psyj6prhhOed_g",{"id":5498,"title":5499,"body":5500,"canonical":788,"date":5504,"description":5505,"extension":786,"featured":787,"image":5506,"meta":5507,"navigation":790,"ogimage":788,"path":5509,"provider":3460,"published":787,"seo":5510,"stem":5511,"tags":5512,"url":5513,"__hash__":5514},"blog/blog/api-mocking-for-your-playwright-tests.md","API Mocking for your Playwright tests",{"type":8,"value":5501,"toc":5502},[],{"title":307,"searchDepth":748,"depth":748,"links":5503},[],"2023-07-04","When working with third party API's it is better to mock the API call rather than hit the API especially when they are API's that you do not control. Mocking the API allows you to finish developing your component or app and write the tests and then when the API is ready you can just swap out the mock for the real API call.","v1630862642/debbie.codes/featured-posts/api-mocking_mvo0sn",{"platform":5508},"Dev.to","/blog/api-mocking-for-your-playwright-tests",{"title":5499,"description":5505},"blog/api-mocking-for-your-playwright-tests",[1412,1411],"https://dev.to/playwright/api-mocking-for-your-playwright-tests-47ah","8Q9cqZMpCqChHjZ7fWbiFkJ7THZMmDWV6gz7V21BSuQ",{"id":5516,"title":5517,"body":5518,"canonical":788,"date":5522,"description":5523,"extension":786,"featured":787,"image":5524,"meta":5525,"navigation":790,"ogimage":788,"path":5527,"provider":3460,"published":787,"seo":5528,"stem":5529,"tags":5530,"url":5532,"__hash__":5533},"blog/blog/ask-engineers-anything.md","GitHub Star Shares Tips on Developer Advocacy, Coding Without a Degree, and Imposter Syndrome",{"type":8,"value":5519,"toc":5520},[],{"title":307,"searchDepth":748,"depth":748,"links":5521},[],"2021-03-10","For February’s Ask Engineers Anything event (aka office hours for our engineer community), I invited GitHub Star and community favorite, Debbie O’Brien, to share her experience as a woman in tech who is making a huge difference in the ecosystem.","f_auto,c_fill,g_auto,e_sharpen/v1615392668/debbie.codes/featured-posts/ask-engineers-anything_m3vmrk",{"loading":3458,"platform":5526},"Andela","/blog/ask-engineers-anything",{"title":5517,"description":5523},"blog/ask-engineers-anything",[5531],"dev-rel","https://andela.com/insights/community-developer-advocacy-impostor-syndrome/","8uWZ20sz-ruUM0YdTSg0xApcfZOjIAXfxYYl-Oer0uc",{"id":5535,"title":5225,"body":5536,"canonical":788,"date":5540,"description":5541,"extension":786,"featured":787,"image":5542,"meta":5543,"navigation":790,"ogimage":788,"path":5544,"provider":5235,"published":787,"seo":5545,"stem":5546,"tags":5547,"url":5548,"__hash__":5549},"blog/blog/autoload-vue-components-in-nuxt.md",{"type":8,"value":5537,"toc":5538},[],{"title":307,"searchDepth":748,"depth":748,"links":5539},[],"2020-06-17","With Nuxt components you can auto import your components really easily and even comes with support for dynamic imports otherwise known as lazy loading. That means you can just add your component in the template without having to add it to the script tag. This makes development much faster.","photo-1587654780291-39c9404d746b?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2550&q=80",{"platform":4232},"/blog/autoload-vue-components-in-nuxt",{"title":5225,"description":5541},"blog/autoload-vue-components-in-nuxt",[5239],"https://vuedose.tips/auto-load-vuejs-components-in-nuxt","75s63c-rAMEnfdA4HwYD2ZSc3qXr95JGEO1XpYsOFCw",{"id":5551,"title":5552,"body":5553,"canonical":5744,"date":5745,"description":5746,"extension":786,"featured":787,"image":788,"meta":5747,"navigation":790,"ogimage":788,"path":5748,"provider":788,"published":790,"seo":5749,"stem":5750,"tags":5751,"url":788,"__hash__":5752},"blog/blog/automate-your-screenshot-documentation-with-playwright-mcp.md","Automate Your Screenshot Documentation with Playwright MCP",{"type":8,"value":5554,"toc":5728},[5555,5558,5561,5565,5568,5571,5574,5578,5586,5589,5595,5598,5602,5605,5622,5626,5633,5650,5653,5657,5660,5663,5666,5670,5674,5677,5681,5684,5688,5691,5695,5698,5700,5703,5707,5725],[11,5556,5557],{},"Creating visual documentation has always been a time-consuming process. As developers and technical writers, we've all experienced the tedious cycle of manually taking screenshots, adding highlights, organizing files, and updating documentation whenever our applications change.",[11,5559,5560],{},"What if I told you there's a way to automate this entire workflow with a single prompt?",[23,5562,5564],{"id":5563},"the-problem-with-manual-screenshot-documentation","The Problem with Manual Screenshot Documentation",[11,5566,5567],{},"Traditional documentation workflows involve:",[11,5569,5570],{},"• Manual navigation through your application\n• Individual screenshots at each step\n• Post-processing to add highlights and annotations\n• File organization and naming\n• Repetitive updates when features change",[11,5572,5573],{},"This process can take time for a simple user flow, and it's often the reason documentation gets outdated quickly.",[23,5575,5577],{"id":5576},"enter-playwright-mcp-server","Enter Playwright MCP Server",[11,5579,2506,5580,5585],{},[15,5581,5584],{"href":5582,"rel":5583},"https://github.com/microsoft/playwright-mcp",[19],"Playwright MCP (Model Context Protocol)"," server changes everything. With one simple prompt, you can generate complete visual documentation automatically.",[11,5587,5588],{},"Here's a simple prompt as an example:",[299,5590,5593],{"className":5591,"code":5592,"language":304},[302],"Navigate to the website and fill in the search term for movie. \nAs you interact with each element, add a red CSS border around \nthe element and take a screenshot.\n",[179,5594,5592],{"__ignoreMap":307},[11,5596,5597],{},"That's it. No complex setup, no scripting required.",[23,5599,5601],{"id":5600},"how-it-works","How It Works",[11,5603,5604],{},"The Playwright MCP server:",[2260,5606,5607,5610,5613,5616,5619],{},[73,5608,5609],{},"Navigates to your specified website",[73,5611,5612],{},"Interacts with elements step by step",[73,5614,5615],{},"Adds CSS highlighting to show user interactions",[73,5617,5618],{},"Captures screenshots automatically at each step",[73,5620,5621],{},"Saves organized files with descriptive names",[138,5623,5625],{"id":5624},"the-technical-magic","The Technical Magic",[11,5627,5628,5629,5632],{},"Behind the scenes, Playwright MCP uses the ",[179,5630,5631],{},"evaluate"," JavaScript tool to inject CSS directly into the page:",[299,5634,5638],{"className":5635,"code":5636,"language":5637,"meta":307,"style":307},"language-javascript shiki shiki-themes github-light github-dark","element.style.border = '3px solid red';\nelement.style.borderRadius = '5px';\n","javascript",[179,5639,5640,5645],{"__ignoreMap":307},[1736,5641,5642],{"class":1738,"line":1739},[1736,5643,5644],{},"element.style.border = '3px solid red';\n",[1736,5646,5647],{"class":1738,"line":748},[1736,5648,5649],{},"element.style.borderRadius = '5px';\n",[11,5651,5652],{},"This creates clear visual indicators of where users should focus their attention.",[23,5654,5656],{"id":5655},"real-world-results","Real-World Results",[11,5658,5659],{},"In the demonstration, the automated process generated:",[11,5661,5662],{},"• Initial page screenshot - The starting state\n• Search element highlighted - Shows where to find the search\n• Search input highlighted - Indicates the active input field\n• Final search results - The completed user journey",[11,5664,5665],{},"Each screenshot is automatically saved with descriptive filenames, ready to drop into your documentation.",[23,5667,5669],{"id":5668},"perfect-use-cases","Perfect Use Cases",[138,5671,5673],{"id":5672},"workshop-documentation","Workshop Documentation",[11,5675,5676],{},"Create step-by-step visual guides for hands-on workshops without spending hours on manual screenshots.",[138,5678,5680],{"id":5679},"user-flow-testing","User Flow Testing",[11,5682,5683],{},"Document complex user journeys for QA teams with consistent visual markers.",[138,5685,5687],{"id":5686},"tutorial-creation","Tutorial Creation",[11,5689,5690],{},"Generate professional-looking tutorial assets that highlight exactly where users should click.",[138,5692,5694],{"id":5693},"product-documentation","Product Documentation",[11,5696,5697],{},"Keep your product docs up-to-date with automated visual guides that regenerate when features change.",[23,5699,3294],{"id":3293},[11,5701,5702],{},"Playwright MCP transforms documentation from a time-consuming chore into an automated process. With one simple prompt, you can generate professional visual guides that would traditionally take hours to create manually.",[23,5704,5706],{"id":5705},"useful-links","Useful Links",[11,5708,5709,5710,5714,5715,5714,5720],{},"• ",[15,5711,5713],{"href":5582,"rel":5712},[19],"Playwright MCP","\n• ",[15,5716,5719],{"href":5717,"rel":5718},"https://youtu.be/uE0r51pneSA",[19],"Playwright MCP + Browser Extension",[15,5721,5724],{"href":5722,"rel":5723},"https://youtu.be/exsikHe20D8",[19],"Install Playwright MCP",[2011,5726,5727],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":5729},[5730,5731,5732,5735,5736,5742,5743],{"id":5563,"depth":748,"text":5564},{"id":5576,"depth":748,"text":5577},{"id":5600,"depth":748,"text":5601,"children":5733},[5734],{"id":5624,"depth":756,"text":5625},{"id":5655,"depth":748,"text":5656},{"id":5668,"depth":748,"text":5669,"children":5737},[5738,5739,5740,5741],{"id":5672,"depth":756,"text":5673},{"id":5679,"depth":756,"text":5680},{"id":5686,"depth":756,"text":5687},{"id":5693,"depth":756,"text":5694},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"https://dev.to/debs_obrien/automate-your-screenshot-documentation-with-playwright-mcp-3gk4","2024-09-03","Creating visual documentation has always been a time-consuming process. What if I told you there's a way to automate this entire workflow with a single prompt using Playwright MCP?",{},"/blog/automate-your-screenshot-documentation-with-playwright-mcp",{"title":5552,"description":5746},"blog/automate-your-screenshot-documentation-with-playwright-mcp",[1412,3321,795],"l_CfI99K6e-ZKqzV-uAAX7JJQ2T_T3Gql1RoqfE7It0",{"id":5754,"title":5755,"body":5756,"canonical":5980,"date":5981,"description":5982,"extension":786,"featured":787,"image":788,"meta":5983,"navigation":790,"ogimage":788,"path":5984,"provider":788,"published":790,"seo":5985,"stem":5986,"tags":5987,"url":788,"__hash__":5988},"blog/blog/automating-form-submissions-with-playwright-mcp-and-a-prompt-file.md","Automating Form Submissions with Playwright MCP and a Prompt file",{"type":8,"value":5757,"toc":5970},[5758,5761,5767,5774,5785,5788,5791,5795,5802,5809,5816,5820,5823,5837,5841,5902,5905,5907,5910,5913,5917,5920,5931,5939,5943,5946,5949,5953,5967],[11,5759,5760],{},"Have you ever wished you could automate browser tasks — like filling out a form or uploading a file — without writing a full-blown test script? What if all you needed was a plain-text prompt written in natural language?",[11,5762,5763,5764,891],{},"Well now you can with the ",[58,5765,5766],{},"Playwright's MCP (Model Context Protocol) server",[11,5768,5769,5770,5773],{},"With just a ",[179,5771,5772],{},".prompt.md"," file in VS Code, I was able to:",[70,5775,5776,5779,5782],{},[73,5777,5778],{},"Fill out a form",[73,5780,5781],{},"Upload an image",[73,5783,5784],{},"Submit it — all hands-free",[11,5786,5787],{},"And I can re-run it any time and easily update the prompt with a different title, date and guest for my event. No rewriting scripts. No code needed.",[11,5789,5790],{},"Let me show you how it works.",[23,5792,5794],{"id":5793},"what-is-mcp","What is MCP?",[11,5796,5797,5798,5801],{},"MCP stands for ",[58,5799,5800],{},"Model Context Protocol",", and it's a new way to give LLMs tools to do things like automate your browser and fill in forms.",[11,5803,5804,5805,5808],{},"Instead of writing JavaScript or TypeScript to control the browser, you write a ",[58,5806,5807],{},"prompt"," — just like you would when chatting with an AI assistant. Behind the scenes the LLM (large language model) interprets your prompt and uses the Playwright MCP server to automate your browser.",[11,5810,5811,5812,5815],{},"You describe ",[133,5813,5814],{},"what"," you want to do, and the MCP server opens a browser and navigates, clicks, types and even uploads an image.",[23,5817,5819],{"id":5818},"the-use-case-form-file-upload","The Use Case: Form + File Upload",[11,5821,5822],{},"Here's what I wanted to automate:",[70,5824,5825,5828,5831,5834],{},[73,5826,5827],{},"Open a webpage with a form I need to fill in every so often",[73,5829,5830],{},"Fill in fields like title, date, social links",[73,5832,5833],{},"Upload a local image file",[73,5835,5836],{},"Click submit",[138,5838,5840],{"id":5839},"i-wrote-a-promptmd-like-this","I wrote a .prompt.md like this:",[299,5842,5845],{"className":5843,"code":5844,"language":786,"meta":307,"style":307},"language-md shiki shiki-themes github-light github-dark","Navigate to https://example.com/form\n\n1. Show: playwright live\n2. Date: 15 July\n3. Time: 1:00 AM\n4. Topic: Playwright Live - Latest updates on Playwright MCP + Live Demo ....\n13. image file `./selfie.png` in the upload section. Click the Submit button when done.\n",[179,5846,5847,5852,5856,5864,5872,5880,5888],{"__ignoreMap":307},[1736,5848,5849],{"class":1738,"line":1739},[1736,5850,5851],{"class":1912},"Navigate to https://example.com/form\n",[1736,5853,5854],{"class":1738,"line":748},[1736,5855,1747],{"emptyLinePlaceholder":790},[1736,5857,5858,5861],{"class":1738,"line":756},[1736,5859,5860],{"class":5036},"1.",[1736,5862,5863],{"class":1912}," Show: playwright live\n",[1736,5865,5866,5869],{"class":1738,"line":1755},[1736,5867,5868],{"class":5036},"2.",[1736,5870,5871],{"class":1912}," Date: 15 July\n",[1736,5873,5874,5877],{"class":1738,"line":1761},[1736,5875,5876],{"class":5036},"3.",[1736,5878,5879],{"class":1912}," Time: 1:00 AM\n",[1736,5881,5882,5885],{"class":1738,"line":1767},[1736,5883,5884],{"class":5036},"4.",[1736,5886,5887],{"class":1912}," Topic: Playwright Live - Latest updates on Playwright MCP + Live Demo ....\n",[1736,5889,5890,5893,5896,5899],{"class":1738,"line":1772},[1736,5891,5892],{"class":5036},"13.",[1736,5894,5895],{"class":1912}," image file ",[1736,5897,5898],{"class":1918},"`./selfie.png`",[1736,5900,5901],{"class":1912}," in the upload section. Click the Submit button when done.\n",[11,5903,5904],{},"That's it.",[23,5906,5601],{"id":5600},[11,5908,5909],{},"I ran the prompt using the Playwright MCP server by clicking the play button in VS Code.",[11,5911,5912],{},"And just like that — the browser opened, filled out the form, uploaded the file, and submitted it for me. All powered by the LLM interpreting the prompt and interacting with the page in real time.",[23,5914,5916],{"id":5915},"easily-repeatable-and-editable","Easily Repeatable and Editable",[11,5918,5919],{},"Here's what makes this workflow magical:",[70,5921,5922,5925,5928],{},[73,5923,5924],{},"✅ I can re-run the same flow anytime.",[73,5926,5927],{},"✏️ I can update the form values by editing a markdown file",[73,5929,5930],{},"🤖 It's fast to prototype browser interactions with natural language",[5932,5933],"iframe",{"width":5934,"height":5935,"src":5936,"title":5937,"frameBorder":1290,"allow":5938,"allowFullScreen":790},560,315,"https://www.youtube.com/embed/NSpCfRDS7vo","Automate Form Filling with a Prompt file and the Playwright MCP","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",[23,5940,5942],{"id":5941},"whats-next","What's Next?",[11,5944,5945],{},"This is just scratching the surface of what the Playwright + MCP can do. I'll be exploring more of these use cases soon.",[11,5947,5948],{},"If you're interested in prompt-driven browser automation or AI + testing/dev tooling, follow along and let me know what you think in the comments. Would love to know what you automate.",[23,5950,5952],{"id":5951},"links","Links",[70,5954,5955,5960],{},[73,5956,5957],{},[15,5958,5713],{"href":5582,"rel":5959},[19],[73,5961,5962],{},[15,5963,5966],{"href":5964,"rel":5965},"https://youtu.be/cifdyJkKs04",[19],"Set up Playwright MCP",[2011,5968,5969],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":5971},[5972,5973,5976,5977,5978,5979],{"id":5793,"depth":748,"text":5794},{"id":5818,"depth":748,"text":5819,"children":5974},[5975],{"id":5839,"depth":756,"text":5840},{"id":5600,"depth":748,"text":5601},{"id":5915,"depth":748,"text":5916},{"id":5941,"depth":748,"text":5942},{"id":5951,"depth":748,"text":5952},"https://dev.to/debs_obrien/automating-form-submissions-with-playwright-mcp-and-a-prompt-file-43c3","2025-07-10","Have you ever wished you could automate browser tasks — like filling out a form or uploading a file — without writing a full-blown test script? What if all you needed was a plain-text prompt written in natural language? Well now you can with Playwright's MCP server.",{},"/blog/automating-form-submissions-with-playwright-mcp-and-a-prompt-file",{"title":5755,"description":5982},"blog/automating-form-submissions-with-playwright-mcp-and-a-prompt-file",[1412,3321,795],"kAjI5AqUrxmTyIpHWS-iDjbdvWaAjvf1w4Y-7ee3p90",{"id":5990,"title":5991,"body":5992,"canonical":788,"date":6208,"description":6209,"extension":786,"featured":787,"image":6210,"meta":6211,"navigation":790,"ogimage":788,"path":6213,"provider":3460,"published":790,"seo":6214,"stem":6215,"tags":6216,"url":788,"__hash__":6217},"blog/blog/being-an-imposter.md","Being an Imposter",{"type":8,"value":5993,"toc":6194},[5994,5997,6000,6003,6007,6010,6013,6016,6020,6023,6026,6029,6032,6036,6039,6042,6048,6051,6055,6058,6062,6076,6079,6082,6088,6091,6095,6098,6102,6105,6109,6112,6115,6119,6122,6125,6128,6134,6138,6141,6144,6147,6153,6157,6160,6163,6169,6172,6175,6179,6182,6185,6188],[11,5995,5996],{},"If you ever look at the home page of my site or the intro slide of my talks you will probably think I am the biggest show off in the world.",[11,5998,5999],{},"I am, and I tell everyone, I am a GitHub Star, a Microsoft Most Valuable Profession, a Google Developer Expert, a Media Developer Expert, a Nuxt ambassador and an Auth0 Ambassador.",[11,6001,6002],{},"However really this is for my benefit, to remind me that I am good enough because for some reason I seem to not see my own achievements and feel like I am not doing enough. I am an imposter. I doubt my abilities and find it difficult to accept my accomplishments and wonder if I deserve them.",[23,6004,6006],{"id":6005},"invincible-at-21","Invincible at 21",[11,6008,6009],{},"At 21 years of age I was invincible. I was in Mallorca working as a kids entertainer earning €120 a week. Yes a week. I had free accommodation and I was having fun so I didn't care. My dream was to be a hollywood star and although I did get to star (be an extra in 2 scenes) alongside Jared Leto, I never actually made it as an actress.",[11,6011,6012],{},"So when I saw a TV series being filmed on the beach I went up to the producer and said I would love to be a runner(someone who runs whatever tasks given to them) for free and help. And so I did. I loved it. And when it finished I asked the producer how can I work in TV. He gave me his business card and said come to London and I will give you work experience in the London Weekend TV Studios.",[11,6014,6015],{},"And so at 21 with no money and no job I moved to London. I got a job in a bar to pay the rent and then worked for free in the TV studios learning as much as I could. I then walked from door to door handing my CV to as many companies as possible. My CV now had London Weekend TV Studios on it. I am not exaggerating when I tell you I handed at least 100 CVs out. But all I needed was one yes and eventually I got it.",[23,6017,6019],{"id":6018},"working-in-the-tv-studios","Working in the TV Studios",[11,6021,6022],{},"I had the most amazing job in the world and I got up every morning excited to go to work. Some of my tasks were simply asking producers and directors what they wanted for lunch and going to restaurants to collect it or making them tea or coffee. I was a runner but we all have to start somewhere. But I was ambitious and I got promoted. I got to work in post production.",[11,6024,6025],{},"Some of my tasks were watching Britney Spears MTV videos to make sure there were no digital flaws before it got released on TV, or working the rack to record 50 copies of a movie to be sent to a airlines and my favourite of all was the Cannes Advertising Awards where I got to create one recording from the many video tapes and manually record a few seconds of black between each Add as well as record the name of the Add and the country. This is how we did things before the digital world existed.",[11,6027,6028],{},"I had an amazing year living in London, earning £7,000 a year, working 80-90 hours a week and on top of that training and flying to Scotland to get my black belt in Taekwondo. Back then to grade for black belt the entry requirement was to break a brick before the 2 day grading began. Crazy to think that I flew on a plane with a brick in my hand so that I could break it and get a black belt.",[11,6030,6031],{},"However I was struggling to survive financially and I was doing too much, and I knew I couldn't keep it up. Making it as an editor in post production was no easy task and I could not survive another year of working so many hours. And so I left London and the TV world behind. But I don't consider it a failure, I consider it as the best year of my life. I achieved so much. I was amazing.",[23,6033,6035],{"id":6034},"seeing-failure-rather-than-success","Seeing failure rather than Success",[11,6037,6038],{},"Now 21 years later and I have achieved so much in tech and yet for some reason I look at the failures more than success. What on earth happened? Sometimes I put it down to life experiences. Things that didn't work out for me then made me less invincible. Who knows.",[11,6040,6041],{},"I had been let go of many companies, I had failed to get a job that lasted more than a year. I really believed that I was bad luck and that any start up that hired me was going to be a failure because of me. My CV was full of companies that did not exist anymore. No trace of them. Was I just making it up? Sure looked that way to potential employers.",[11,6043,6044],{},[121,6045],{"alt":6046,"src":6047},"me launching site at a festival","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648378431/debbie.codes/blog/2022/E4654BC5-6FB7-48DE-9B57-8D2569E7AA2E_1_105_c_crkcyc.jpg",[11,6049,6050],{},"My freelance work started with knocking on restaurant doors asking for work to create their website, their menus, whatever. And I created some amazing sites in Flash, a technology that now no longer exists, can't put that on the CV.",[23,6052,6054],{"id":6053},"at-the-point-of-giving-up","At the point of giving up",[11,6056,6057],{},"I was at the point of just giving up in tech. And so I signed up with a University to get a degree online studying Spanish language so I could work in the Spanish schools as a real teacher rather than giving classes on Sunday mornings and working in language schools for €1,000 a month. The uni degree actually taught me a lot. It was during this degree that I learnt how to prepare and give presentations among other things. I had never been to Uni before. But during my first year I knew that this wasn't the path I wanted to go down and I wasn't happy in life in general. Everything was going wrong and I felt like I was just in a big deep dark hole.",[23,6059,6061],{"id":6060},"give-tech-one-more-chance","Give Tech one more chance",[11,6063,6064,6065,6070,6071,891],{},"Exactly 5 years ago today I decided to give tech one more go. I had plan B to be a teacher already in motion so now it was time to aim for plan A. I gave up my job in the language school and used up all my savings to pay for my online Uni fees, my ",[15,6066,6069],{"href":6067,"rel":6068},"https://openclassrooms.com/en/",[19],"online frontend tech degree"," and my online ",[15,6072,6075],{"href":6073,"rel":6074},"https://teamtreehouse.com/techdegree/full-stack-javascript",[19],"fullstack tech degree",[11,6077,6078],{},"I studied and struggled to pay the bills for about 8 months. I had no idea if it would be worth it. If I would get a job or be successful. I just knew I had to give it one more go before I threw in the towel. It was hard. Learning JavaScript was hard, is hard. I had failed at it many times before and had convinced myself that I was not good enough to be a real programmer.",[11,6080,6081],{},"But I really wanted to make it. As much as it was hard I really enjoyed it. I was studying 12 hours a day, even while running on the running machine at the gym. I needed to succeed. I didn't want to go back to the life I had. I didn't want to struggle anymore and have to work weekends to pay the bills. I wanted to love my job and be challenged each day and coding gave me that. If only I could learn JavaScript.",[11,6083,6084],{},[121,6085],{"alt":6086,"src":6087},"me studying while running at gym","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648377845/debbie.codes/blog/2022/DF5378DD-E525-4B02-A898-F7EAF836C910_1_105_c_ddsr6y.jpg",[11,6089,6090],{},"Both tech degrees I did were extremely challenging and come with real life projects. I was building things constantly. I was pair programming with others to solve problems together. And I had a mentor who not just went over my code and showed me how I could improve it, but was there for me to remind me of how well I was doing and that I was good enough, that it isn't easy but that I can do it and I was going to learn JavaScript.",[23,6092,6094],{"id":6093},"the-nos-are-always-hard","The No's are always hard",[11,6096,6097],{},"I got many no's from interviews including one where I just wanted the ground to swallow me up. I panicked. The process was horrible and I was just terrified and my brain didn't work that day at all. I couldn't even use the mouse and when they asked me to go to the white board to do a coding challenge, I just refused. Obviously I did not get that job. But I walked out of there and said I am never going to be in that position again where I am given code that I have not seen before and panic. And so I started peer reviewing other peoples code. At least one peer review a day and became the student with the most peer reviews ever.",[23,6099,6101],{"id":6100},"i-got-the-job","I got the job",[11,6103,6104],{},"Then in late October 2017, I got a job. I had to do a coding challenge and I found the challenge pretty easy. I had done very similar stuff in my tech degree so basically I was prepared. I was ready. And they were super impressed with how fast and well I did the challenge and how clean my code was. And so I got my job as a real frontend programmer. I was in shock. I called my husband and said \"I think I just got a job\".",[23,6106,6108],{"id":6107},"afraid-i-was-going-to-get-sacked","Afraid I was going to get sacked",[11,6110,6111],{},"And that is where the imposter syndrome began. I went to work every day hoping not to get sacked. I didn't know everything. People kept asking me for help with JavaScript stuff. This was my first JavaScript job. I was freaking out. I got moved into the architectural part of the company and tasked with solving frontend scaling problems which ended up with us creating a custom framework, design system and component library.",[11,6113,6114],{},"At one point I sent a message to my boss asking him to please give me a warning before he sacked me so I could work harder and try to improve cause I really loved my job and didn't want to lose it. My boss was amazing. He told me why on earth would we even consider sacking you. You are incredible, he said and everyone is so impressed with you. And then he sent me some articles on imposter syndrome and every now and again he would send me interviews or short videos to help me overcome it and to show me I was not alone.",[23,6116,6118],{"id":6117},"feeling-the-imposter","Feeling the Imposter",[11,6120,6121],{},"You would think that it just goes away, but it doesn't. When I got awarded Google Developer Expert I really thought that they had made a mistake. I was waiting for the email to say it was a mistake. Seriously. I was no expert. That's a big word that carries a lot of weight. I now mentor for the Google Developer Expert program and remind other women that the word expert does not mean you have to be an expert in everything. You are an expert in one area, perhaps it's CSS animations, perhaps it's performance but not everything. None of us are experts in everything.",[11,6123,6124],{},"I then got my MVP Award and again I thought, wow, is this for real. I had been referred by others for both of these award programs and in the space of a a few months I was a GDE and an MVP and it was just insane. I never thought to look back on all the work I had been doing in the community. All the posts I had written, talks I had given, projects I had created that were open source, mentoring I was doing for both programs I had graduated from. All I saw was what I had not yet learnt and there was so much.",[11,6126,6127],{},"When I got my GitHub star I was in shock. People had voted for me. They thought I was a star. They knew I existed. Wow. Now I was freaking out even more. I am not a star. I know way less than anyone else. Everyone else is way more clever than me. They are going to soon find out and kick me out of the program. I am sure of it.",[11,6129,6130],{},[121,6131],{"alt":6132,"src":6133},"my and my awards","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1619366231/debbie.codes/blog/IMG_8224_pe76ma.jpg",[23,6135,6137],{"id":6136},"overcoming-imposter-syndrome","Overcoming Imposter Syndrome",[11,6139,6140],{},"The best way to get over imposter syndrome is to talk about it. And so I did. But the feelings are had to control. When it came to the GitHub Nova conference I reached out and said, I really want to give a talk but I am terrified of giving a talk in front of so many GitHub stars. To me they were the stars. I was the imposter. But I got reminded of all the work I had done for the community, how skilled I am and why I was in the program and that yes I was a star.",[11,6142,6143],{},"I have all my awards behind me in my office. They are my reminder that I got this. I am amazing. Sometimes I need more reminders. I wear my GitHub stars t-shirt way too often. It's my favourite. In the entrance of my apartment I have two walls full of signs that remind me I am awesome, to not give up, to keep dreaming, to do more of what makes me happy and to believe in myself.",[11,6145,6146],{},"Sometimes before an interview or a talk I literally read through them all, some of them twice, and then I say come on Debbie, you can do this. And the best thing I ever did was program Alexa to tell me I am amazing. She never fails.",[11,6148,6149],{},[121,6150],{"alt":6151,"src":6152},"signs in my entrance with positive messages","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648378993/debbie.codes/blog/2022/4AD3BD54-DC34-4CDB-B746-39CC9323765D_blkyz5.heic",[23,6154,6156],{"id":6155},"comparing-yourself-to-others","Comparing yourself to others",[11,6158,6159],{},"As we get more successful in tech we become even more imposters. If I compare myself to my family I am an expert, a star, everything. If I compare myself to my tech idols, I am no where near an expert. But this is such an unfair comparison. It is like comparing a Taekwondo black belt to a white belt. The black belt is the expert here. But anyone who does martial arts will know that once you get your black belt you then start comparing yourself to the 2nd degrees, 3rd degrees, masters, and all of a sudden you start to feel almost like a white belt again.",[11,6161,6162],{},"This is the great thing about martial arts, and the great thing about tech. There is so much to learn that you will never know it all, never perfect it all. You can't get your black belt just by training for a year, no matter how good you are at your kicks for example. It takes time. A lot of hard work and time.",[11,6164,6165],{},[121,6166],{"alt":6167,"src":6168},"me doing a spinning kick in front of Cathedral","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648376409/debbie.codes/blog/2022/A26430C9-6959-45BF-83B7-EB0086E85E6A_1_105_c_occakl.jpg",[11,6170,6171],{},"To go from 3rd degree to 4th degree I had to wait 4 years. I had to train hard, study, practice and then grade. My grading was 7 hours long. I flew to London to grade with one of the Grand Masters, a 9th degree and one of the best in the world. He told us that we would go through everything from patterns, to breaking with flying kicks, sparring, theory etc and that he was going to push us so hard that we will want to give up. That, he said, is when the grading really begins. And he was not joking.",[11,6173,6174],{},"The last half hour after almost 7 hours of grading we had sparring and not just 1 on 1 but 2 against 1. The goal here was not to win, not to show off your amazing spinning kicks, it was to show that you could continue when your body wanted to give up. It was to show that if you put your mind to it you really can achieve anything. And I did. I got my 4th degree 3 years ago. But more importantly was the lesson I learnt that day. Never give up no matter how hard it gets. You really can do anything if you put your mind to it. And if you fail it doesn't mean you are a failure, it just means you lack preparation. It means work on your weak areas, improve and then try again.",[23,6176,6178],{"id":6177},"the-journey-takes-time","The Journey takes time",[11,6180,6181],{},"When it comes to tech it's the same. It's a journey and it takes time. It takes time to learn, it takes time to master and it takes time to get to the next stage, whatever that stage might be. And above all it takes a lot of hard work and determination. But just like anyone can get a black belt in Taekwondo, if they work hard enough, anyone can learn to code and get a job in tech, if they work hard enough.",[11,6183,6184],{},"And remember the majority of us in tech suffer from imposter syndrome so you are not alone. The best advice I can give you is to talk about it. Be open and honest with others about it, especially your boss or colleagues at work. My DM's are always open if you ever need someone to talk to.",[11,6186,6187],{},"You got this!",[11,6189,6190],{},[121,6191],{"alt":6192,"src":6193},"me running up a mountain","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1640971055/debbie.codes/blog/running-wicklow-mountains_c_n7mvet.jpg",{"title":307,"searchDepth":748,"depth":748,"links":6195},[6196,6197,6198,6199,6200,6201,6202,6203,6204,6205,6206,6207],{"id":6005,"depth":748,"text":6006},{"id":6018,"depth":748,"text":6019},{"id":6034,"depth":748,"text":6035},{"id":6053,"depth":748,"text":6054},{"id":6060,"depth":748,"text":6061},{"id":6093,"depth":748,"text":6094},{"id":6100,"depth":748,"text":6101},{"id":6107,"depth":748,"text":6108},{"id":6117,"depth":748,"text":6118},{"id":6136,"depth":748,"text":6137},{"id":6155,"depth":748,"text":6156},{"id":6177,"depth":748,"text":6178},"2022-03-27","If you ever look at the home page of my site or the intro slide of my talks you will probably think I am the biggest show off in the world. However, really I am an imposter. I doubt my abilities and find it difficult to accept my accomplishments and wonder if I deserve them.","v1648376409/debbie.codes/blog/2022/A26430C9-6959-45BF-83B7-EB0086E85E6A_1_105_c_occakl.jpg",{"ogImage":6212,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648376409/debbie.codes/blog/2022/A26430C9-6959-45BF-83B7-EB0086E85E6A_1_105_c_occakl.jpg","/blog/being-an-imposter",{"title":5991,"description":6209},"blog/being-an-imposter",[3464,5495],"4O8jUe1289IPgRFe4fga2NAhwWqQRjY-TAHRh-FXOoA",{"id":6219,"title":6220,"body":6221,"canonical":788,"date":6243,"description":6244,"extension":786,"featured":787,"image":6245,"meta":6246,"navigation":790,"ogimage":788,"path":6248,"provider":3460,"published":790,"seo":6249,"stem":6250,"tags":6251,"url":788,"__hash__":6252},"blog/blog/brought-my-babies-to-a-conference.md","Taking my babies to a conference",{"type":8,"value":6222,"toc":6241},[6223,6226,6229,6232,6235,6238],[11,6224,6225],{},"I just got back from my first conference since being a mom. And it was amazing. I had forgotten how much I had missed hanging out with my speaker friends. I connect with them so much more than the people I see every day. They get me and I feel I can just be myself around them. They also give me so much energy that being around other speakers is like a mega battery recharge for me. And being on stage, giving talks, and connecting with the audience is so much better in person than remote.",[11,6227,6228],{},"But this time it was different. I brought my babies to the conference. I was really worried about doing this as what would people think? And the voices start going off in my head. But why should I care. Cause you know, I just do. I am always just trying to fit in and the thoughts of standing out from the crowd is hard for me. Now try blending in when going to a conference with not one but two babies. yeh.",[11,6230,6231],{},"The first morning I was actually terrified. I was at breakfast in the hotel and my 8 month old twins are just really starting on solids and one decided he didn't really want to eat which meant he was going to need to be breastfed instead. I have breastfed in public before from supermarkets to the sea, but this was different. I was the only woman in the whole room and for a moment I felt uncomfortable and scared to do what I normally do. But I took a deep breath and said breastfeeding needs to be normalized, and I just fed him, and it was fine. To be honest I don't think anyone even noticed.",[11,6233,6234],{},"Then I arrived at the venue pushing a double buggy through the hall. But the response I got from people was great, and it really did make me feel like it was ok for me to be here with them. You might ask why I brought them in the first place. The answer is simple. They are now part of my life and I have no intention of leaving them behind. Plus I am still breastfeeding, and I don't have anyone else who can look after them besides my husband, who traveled with me to the conference.And two babies is not one baby so it's just harder to leave them with someone else. Basically I am not ready to.",[11,6236,6237],{},"So should kids be allowed at a conference? I have done way less speaking events because of being a mom. That is a fact. And now that I have twins I can't fly with two babies on my own so each time I go to a conference it costs me money to pay for my husbands flight, so I have to be careful when choosing which conference to go to plus I also have to think about are the flights direct and if not how long is the whole journey and at what time are the flights. So much to think about. But if I can attend a conference as a speaker and have my babies close by, so I can feed them when needed and just be there if they need me or to help out my husband, then it makes things so much easier. So why not.",[11,6239,6240],{},"I feel we can really improve the diversity at conference just by making things like this easier. I spoke with other speakers while there about this, and we talked about day care at conferences. This is a great idea but not always possible. Sometimes it could be as simple as just saying there are baby changing facilities or a parents room available. This just makes it inviting and means it's ok if you need/want to bring your baby. Let's normalize this. Let's just make things easier for parents and especially for women in tech.",{"title":307,"searchDepth":748,"depth":748,"links":6242},[],"2024-09-02","But this time it was different. I brought my babies to the conference. I was really worried about doing this as what would people think? And the voices start going off in my head. But why should I care. Cause you know, I just do.","v1725569430/debbie.codes/blog/2024/GWQjxLBW0AEuweB_eulios.jpg",{"ogImage":6247},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1725569430/debbie.codes/blog/2024/","/blog/brought-my-babies-to-a-conference",{"title":6220,"description":6244},"blog/brought-my-babies-to-a-conference",[3464],"ika-NJGguKnkpOwU-Ed7pZdiK_I9pLoDmquDxZ0idkg",{"id":6254,"title":6255,"body":6256,"canonical":788,"date":6260,"description":6261,"extension":786,"featured":787,"image":6262,"meta":6263,"navigation":790,"ogimage":788,"path":6265,"provider":5235,"published":787,"seo":6266,"stem":6267,"tags":6268,"url":6269,"__hash__":6270},"blog/blog/build-counter-dropdown-accordion.md","Build a Counter, Dropdown and Accordion in Vue Components!",{"type":8,"value":6257,"toc":6258},[],{"title":307,"searchDepth":748,"depth":748,"links":6259},[],"2019-08-21","Let’s dive into building some core main components that you might be tasked with when writing applications in Vue. Or, if you’re new to Vue, then these are definitely some great ways to get started understanding the more ‘common’ components that we might work with on a day-to-day basis. With that in mind, let’s get started right away with building a Counter, Dropdown and finally an Accordion with Vue!","photo-1528840942-6d0562674ad6?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1700&q=80",{"platform":6264},"Ultimate Courses","/blog/build-counter-dropdown-accordion",{"title":6255,"description":6261},"blog/build-counter-dropdown-accordion",[5240],"https://ultimatecourses.com/blog/build-a-counter-dropdown-and-accordion-in-vue-components","MHSVrgATB_HQjbiWA8iNrRNm9a4MCZQx8UuBffzi05o",{"id":2727,"title":2728,"body":6272,"canonical":2848,"date":2849,"description":2850,"extension":786,"featured":787,"image":788,"meta":6349,"navigation":790,"ogimage":788,"path":2852,"provider":788,"published":790,"seo":6350,"stem":2854,"tags":6351,"url":788,"__hash__":2856},{"type":8,"value":6273,"toc":6340},[6274,6276,6278,6280,6285,6289,6291,6293,6295,6297,6299,6301,6303,6305,6307,6309,6311,6313,6315,6317,6319,6321,6323,6325,6327,6329,6331,6336,6338],[11,6275,2733],{},[11,6277,2736],{},[23,6279,2740],{"id":2739},[11,6281,2743,6282,2749],{},[15,6283,2748],{"href":2746,"rel":6284},[19],[11,6286,2752,6287,2756],{},[58,6288,2755],{},[11,6290,2759],{},[23,6292,2763],{"id":2762},[11,6294,2766],{},[11,6296,2769],{},[11,6298,2772],{},[23,6300,2776],{"id":2775},[11,6302,2779],{},[11,6304,2782],{},[23,6306,2786],{"id":2785},[11,6308,2789],{},[11,6310,2792],{},[11,6312,2795],{},[23,6314,2799],{"id":2798},[11,6316,2802],{},[11,6318,2805],{},[23,6320,2809],{"id":2808},[11,6322,2812],{},[11,6324,2815],{},[11,6326,2818],{},[23,6328,2822],{"id":2821},[11,6330,2825],{},[11,6332,2828,6333,2832],{},[15,6334,2748],{"href":2746,"rel":6335},[19],[11,6337,2835],{},[11,6339,2838],{},{"title":307,"searchDepth":748,"depth":748,"links":6341},[6342,6343,6344,6345,6346,6347,6348],{"id":2739,"depth":748,"text":2740},{"id":2762,"depth":748,"text":2763},{"id":2775,"depth":748,"text":2776},{"id":2785,"depth":748,"text":2786},{"id":2798,"depth":748,"text":2799},{"id":2808,"depth":748,"text":2809},{"id":2821,"depth":748,"text":2822},{},{"title":2728,"description":2850},[795,796],{"id":6353,"title":6354,"body":6355,"canonical":788,"date":8674,"description":8675,"extension":786,"featured":787,"image":8676,"meta":8677,"navigation":790,"ogimage":788,"path":8678,"provider":3460,"published":787,"seo":8679,"stem":8680,"tags":8681,"url":788,"__hash__":8682},"blog/blog/building-an-ecommerce-product-page.md","Building an ecommerce Product details component",{"type":8,"value":6356,"toc":8650},[6357,6360,6363,6367,6377,6380,6384,6387,6401,6405,6408,6412,6415,6419,6422,6431,6435,6438,6505,6508,6512,6515,6518,6522,6525,6534,6543,6547,6580,6583,6758,6762,6765,6962,6965,7046,7050,7053,7056,7313,7316,7403,7406,7410,7419,7423,7437,7440,7613,7617,7620,7623,7632,7636,7640,7643,7646,7650,7653,7657,7661,7664,7961,7964,7978,7981,8114,8118,8121,8124,8127,8282,8285,8288,8315,8318,8332,8335,8485,8488,8490,8493,8497,8500,8504,8507,8510,8513,8517,8520,8579,8582,8585,8589,8592,8595,8598,8603,8607,8610,8647],[11,6358,6359],{},"I started off with a basic demo of a shoe store which uses components from different scopes including the base-ui scope for base/design components and the e-commerce scope for components that are more e-commerce focused and therefore owned by the e-commerce team. Scopes are a collection of components and can have but do not need to have their own repository.",[11,6361,6362],{},"It was time to enhance the shoe store and add the detail page for when you click from the shoe's product card. This page should give you more details of the product and allow you to choose what colour and size you want as well as the quantity and finally have a call to action that will add the product to a cart. This page needs to be a dynamic page and render the correct product when clicked.",[23,6364,6366],{"id":6365},"so-where-do-we-start","So where do we start?",[6368,6369,6376],"nuxt-img",{"provider":3460,"src":6370,"format":6371,"quality":6372,"fit":6373,"alt":6374,"loading":6375},"debbie.codes/blog/product-details-component_2x_isk5aj","auto","100","cover","Product Details component in Bit","lazy","\n \n",[11,6378,6379],{},"First of all we have a design that shows us what the page should look like. From this design we can break things up into components starting with the biggest component first which is the page component. Then we need to ask ourselves where does this page live? To which scope does it belong to or which team is responsible for it? As we are building a page in the shoe store it was pretty obvious that this page component should belong to the shoe-store scope.",[138,6381,6383],{"id":6382},"breaking-down-the-product-page","Breaking down the Product Page",[11,6385,6386],{},"Now when we start breaking up the page into composable components we have 4 components that we need to create:",[70,6388,6389,6392,6395,6398],{},[73,6390,6391],{},"the product details component",[73,6393,6394],{},"the reviews component",[73,6396,6397],{},"the featured products component",[73,6399,6400],{},"the banner component.",[23,6402,6404],{"id":6403},"product-details-component","Product Details Component",[11,6406,6407],{},"For this component we need to now break it down into smaller components and decide who each component belongs too, who should build and own these components and what their names should be. It is at this stage that we also check to see if we already have a component created that we can use or if we need to create a new component or perhaps even enhance an already created component.",[138,6409,6411],{"id":6410},"naming-and-scopes","Naming and Scopes",[11,6413,6414],{},"We decided that the product details component should be named 'product-details' and should be owned by the e-commerce team as this component can be used not just in the shoe store but also in any of the other stores.",[138,6416,6418],{"id":6417},"api-first","API First",[11,6420,6421],{},"We then work out the props needed for this component which in our case is:",[299,6423,6425],{"className":4894,"code":6424,"language":4896,"meta":307,"style":307},"src, alt, title, rating, price, text, availableSizes, availableColors\n",[179,6426,6427],{"__ignoreMap":307},[1736,6428,6429],{"class":1738,"line":1739},[1736,6430,6424],{"class":1912},[138,6432,6434],{"id":6433},"listing-out-our-components","Listing out our Components",[11,6436,6437],{},"The components needed to create the product details component are:",[70,6439,6440,6443,6446,6452,6455,6458,6473,6485,6488,6497],{},[73,6441,6442],{},"Img component from base-ui",[73,6444,6445],{},"heading component from base-ui",[73,6447,6448,6449],{},"rating component from ecommerce ",[58,6450,6451],{},"(new)",[73,6453,6454],{},"currency component from ecommerce",[73,6456,6457],{},"text component from base-ui",[73,6459,6460,6461,6463],{},"counter component from ecommerce ",[58,6462,6451],{},[70,6464,6465,6468],{},[73,6466,6467],{},"button component from base-ui",[73,6469,6470,6471],{},"input component from base-ui ",[58,6472,6451],{},[73,6474,6475,6476,6478],{},"select size component from ecommerce ",[58,6477,6451],{},[70,6479,6480],{},[73,6481,6482,6483],{},"select component from base-ui ",[58,6484,6451],{},[73,6486,6487],{},"available colors component from base-ui",[73,6489,6490,6491,6493],{},"addToCart component from ecommerce ",[58,6492,6451],{},[70,6494,6495],{},[73,6496,6467],{},[73,6498,6499,6500],{},"shoes entity component\n",[70,6501,6502],{},[73,6503,6504],{},"product entity component",[11,6506,6507],{},"Once we have our components we need to start thinking about the api that we need to build for each of these components. What props do we need in order to build these components?",[138,6509,6511],{"id":6510},"thinking-in-apis","Thinking in APIs",[11,6513,6514],{},"The rating component needs props of rating. Does it need a stars prop? Or are all ratings stars? These are decisions that we need to make in alignment with the product owner. It might not be necessary to build a rating component that does ratings for any type of ratings. Stars might be enough for this project. It can always be refactored later if needs be with a default of stars so no breaking changes take place.",[11,6516,6517],{},"What is important is to build what we need and not build for every single possible future need that may or may not happen. It is very easy to over architect a project and end up with a big mess that you have to deal with later. It is not always easy to only build what you need yet build in a way that can easily scale if and when you need to",[138,6519,6521],{"id":6520},"time-to-build","Time to Build",[11,6523,6524],{},"Once we are clear on how we are going to build the component it is then possible to actually build it. There is no right or wrong way when it comes to building but I will share with you how I built it. Of course I built this alone but if working with a team then certain members of the team might be building some of the components and then they can just be pieced together.",[11,6526,6527,6528,6533],{},"I also used ",[15,6529,6532],{"href":6530,"rel":6531},"http://Bit.dev",[19],"Bit.dev"," to build my components but this is not essential to architecting your site and if you don't want to have independent components you can still use this way of thinking to build no matter what framework or library you are using. However the reason I use Bit is so I can build these components independently and in isolation from the other components. This makes me be able to build, see and test the component in its own environment before using it in the more complex component. That way it can easily be re-used in other components as it is fully decoupled from any other component yet can have dependencies of other components if needed.",[11,6535,6536,6537,6542],{},"We start by building the product-details component in the ecommerce scope. The next component we need is the ",[15,6538,6541],{"href":6539,"rel":6540},"https://bit.dev/learn-bit-react/base-ui/ui/img",[19],"Image component"," and we already have one created in the base-ui scope so we can just go to that and install it into our project using bit, npm or yarn.",[6368,6544,6376],{"provider":3460,"src":6545,"format":6371,"quality":6372,"fit":6373,"alt":6546,"loading":6375},"debbie.codes/blog/Img-component_2x_uxxmep","Image Component in Bit",[299,6548,6550],{"className":2665,"code":6549,"language":2667,"meta":307,"style":307},"bit install @learn-bit-react/base-ui.ui.img\nnpm i @learn-bit-react/base-ui.ui.img\nyarn add @learn-bit-react/base-ui.ui.img\n",[179,6551,6552,6561,6571],{"__ignoreMap":307},[1736,6553,6554,6556,6558],{"class":1738,"line":1739},[1736,6555,4970],{"class":2674},[1736,6557,4973],{"class":1935},[1736,6559,6560],{"class":1935}," @learn-bit-react/base-ui.ui.img\n",[1736,6562,6563,6566,6569],{"class":1738,"line":748},[1736,6564,6565],{"class":2674},"npm",[1736,6567,6568],{"class":1935}," i",[1736,6570,6560],{"class":1935},[1736,6572,6573,6576,6578],{"class":1738,"line":756},[1736,6574,6575],{"class":2674},"yarn",[1736,6577,2681],{"class":1935},[1736,6579,6560],{"class":1935},[11,6581,6582],{},"We then import the component and start composing our product details component.",[299,6584,6586],{"className":4894,"code":6585,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { Img } from '@learn-bit-react/base-ui.ui.img'\nimport styles from './product-details.module.scss'\n\nexport type ProductDetailsProps = {} & React.HTMLAttributes\u003CHTMLDivElement>\n\nexport function ProductDetails() {\n  return (\n    \u003Cdiv className={styles.productDetails}>\n      \u003CImg className={styles.img} src=\"some-img-url\" alt=\"alt\" />\n    \u003C/div>\n  )\n}\n",[179,6587,6588,6600,6612,6624,6628,6664,6668,6681,6689,6706,6740,6749,6754],{"__ignoreMap":307},[1736,6589,6590,6592,6595,6597],{"class":1738,"line":1739},[1736,6591,4996],{"class":4866},[1736,6593,6594],{"class":1912}," React ",[1736,6596,5002],{"class":4866},[1736,6598,6599],{"class":1935}," 'react'\n",[1736,6601,6602,6604,6607,6609],{"class":1738,"line":748},[1736,6603,4996],{"class":4866},[1736,6605,6606],{"class":1912}," { Img } ",[1736,6608,5002],{"class":4866},[1736,6610,6611],{"class":1935}," '@learn-bit-react/base-ui.ui.img'\n",[1736,6613,6614,6616,6619,6621],{"class":1738,"line":756},[1736,6615,4996],{"class":4866},[1736,6617,6618],{"class":1912}," styles ",[1736,6620,5002],{"class":4866},[1736,6622,6623],{"class":1935}," './product-details.module.scss'\n",[1736,6625,6626],{"class":1738,"line":1755},[1736,6627,1747],{"emptyLinePlaceholder":790},[1736,6629,6630,6633,6636,6639,6641,6644,6647,6650,6652,6655,6658,6661],{"class":1738,"line":1761},[1736,6631,6632],{"class":4866},"export",[1736,6634,6635],{"class":4866}," type",[1736,6637,6638],{"class":2674}," ProductDetailsProps",[1736,6640,4911],{"class":4866},[1736,6642,6643],{"class":1912}," {} ",[1736,6645,6646],{"class":4866},"&",[1736,6648,6649],{"class":2674}," React",[1736,6651,891],{"class":1912},[1736,6653,6654],{"class":2674},"HTMLAttributes",[1736,6656,6657],{"class":1912},"\u003C",[1736,6659,6660],{"class":2674},"HTMLDivElement",[1736,6662,6663],{"class":1912},">\n",[1736,6665,6666],{"class":1738,"line":1767},[1736,6667,1747],{"emptyLinePlaceholder":790},[1736,6669,6670,6672,6675,6678],{"class":1738,"line":1772},[1736,6671,6632],{"class":4866},[1736,6673,6674],{"class":4866}," function",[1736,6676,6677],{"class":2674}," ProductDetails",[1736,6679,6680],{"class":1912},"() {\n",[1736,6682,6683,6686],{"class":1738,"line":1778},[1736,6684,6685],{"class":4866},"  return",[1736,6687,6688],{"class":1912}," (\n",[1736,6690,6691,6694,6698,6701,6703],{"class":1738,"line":1784},[1736,6692,6693],{"class":1912},"    \u003C",[1736,6695,6697],{"class":6696},"s9eBZ","div",[1736,6699,6700],{"class":2674}," className",[1736,6702,5062],{"class":4866},[1736,6704,6705],{"class":1912},"{styles.productDetails}>\n",[1736,6707,6708,6711,6714,6716,6718,6721,6724,6726,6729,6732,6734,6737],{"class":1738,"line":1790},[1736,6709,6710],{"class":1912},"      \u003C",[1736,6712,6713],{"class":1918},"Img",[1736,6715,6700],{"class":2674},[1736,6717,5062],{"class":4866},[1736,6719,6720],{"class":1912},"{styles.img} ",[1736,6722,6723],{"class":2674},"src",[1736,6725,5062],{"class":4866},[1736,6727,6728],{"class":1935},"\"some-img-url\"",[1736,6730,6731],{"class":2674}," alt",[1736,6733,5062],{"class":4866},[1736,6735,6736],{"class":1935},"\"alt\"",[1736,6738,6739],{"class":1912}," />\n",[1736,6741,6742,6745,6747],{"class":1738,"line":1796},[1736,6743,6744],{"class":1912},"    \u003C/",[1736,6746,6697],{"class":6696},[1736,6748,6663],{"class":1912},[1736,6750,6751],{"class":1738,"line":2353},[1736,6752,6753],{"class":1912},"  )\n",[1736,6755,6756],{"class":1738,"line":2358},[1736,6757,1976],{"class":1912},[138,6759,6761],{"id":6760},"building-things-with-props","Building things with props",[11,6763,6764],{},"Now there are a few ways of doing things. You will notice I have just added a static src and alt into the image component. This just allows me to quickly see what I am doing. However it is also possible to directly use props here and start building out the composition file with the mock data. This is how we build things in Bit as the composition file is how we can see the component while running Bits dev server.",[299,6766,6768],{"className":4894,"code":6767,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { Img } from '@learn-bit-react/base-ui.ui.img'\nimport styles from './product-details.module.scss'\n\nexport type ProductDetailsProps = {\n  /**\n   * source of the image\n   */\n  src: string,\n  /**\n   * alt of the image\n   */\n  alt: string\n} & React.HTMLAttributes\u003CHTMLDivElement>\n\nexport function ProductDetails() {\n  return (\n    \u003Cdiv className={styles.productDetails}>\n      \u003CImg className={styles.img} src={src} alt={alt} />\n    \u003C/div>\n  )\n}\n",[179,6769,6770,6780,6790,6800,6804,6816,6822,6827,6832,6844,6848,6853,6857,6867,6886,6890,6900,6906,6918,6945,6953,6957],{"__ignoreMap":307},[1736,6771,6772,6774,6776,6778],{"class":1738,"line":1739},[1736,6773,4996],{"class":4866},[1736,6775,6594],{"class":1912},[1736,6777,5002],{"class":4866},[1736,6779,6599],{"class":1935},[1736,6781,6782,6784,6786,6788],{"class":1738,"line":748},[1736,6783,4996],{"class":4866},[1736,6785,6606],{"class":1912},[1736,6787,5002],{"class":4866},[1736,6789,6611],{"class":1935},[1736,6791,6792,6794,6796,6798],{"class":1738,"line":756},[1736,6793,4996],{"class":4866},[1736,6795,6618],{"class":1912},[1736,6797,5002],{"class":4866},[1736,6799,6623],{"class":1935},[1736,6801,6802],{"class":1738,"line":1755},[1736,6803,1747],{"emptyLinePlaceholder":790},[1736,6805,6806,6808,6810,6812,6814],{"class":1738,"line":1761},[1736,6807,6632],{"class":4866},[1736,6809,6635],{"class":4866},[1736,6811,6638],{"class":2674},[1736,6813,4911],{"class":4866},[1736,6815,4914],{"class":1912},[1736,6817,6818],{"class":1738,"line":1767},[1736,6819,6821],{"class":6820},"sJ8bj","  /**\n",[1736,6823,6824],{"class":1738,"line":1772},[1736,6825,6826],{"class":6820},"   * source of the image\n",[1736,6828,6829],{"class":1738,"line":1778},[1736,6830,6831],{"class":6820},"   */\n",[1736,6833,6834,6837,6839,6842],{"class":1738,"line":1784},[1736,6835,6836],{"class":5036},"  src",[1736,6838,1087],{"class":4866},[1736,6840,6841],{"class":1918}," string",[1736,6843,1939],{"class":1912},[1736,6845,6846],{"class":1738,"line":1790},[1736,6847,6821],{"class":6820},[1736,6849,6850],{"class":1738,"line":1796},[1736,6851,6852],{"class":6820},"   * alt of the image\n",[1736,6854,6855],{"class":1738,"line":2353},[1736,6856,6831],{"class":6820},[1736,6858,6859,6862,6864],{"class":1738,"line":2358},[1736,6860,6861],{"class":5036},"  alt",[1736,6863,1087],{"class":4866},[1736,6865,6866],{"class":1918}," string\n",[1736,6868,6869,6872,6874,6876,6878,6880,6882,6884],{"class":1738,"line":2364},[1736,6870,6871],{"class":1912},"} ",[1736,6873,6646],{"class":4866},[1736,6875,6649],{"class":2674},[1736,6877,891],{"class":1912},[1736,6879,6654],{"class":2674},[1736,6881,6657],{"class":1912},[1736,6883,6660],{"class":2674},[1736,6885,6663],{"class":1912},[1736,6887,6888],{"class":1738,"line":2370},[1736,6889,1747],{"emptyLinePlaceholder":790},[1736,6891,6892,6894,6896,6898],{"class":1738,"line":2376},[1736,6893,6632],{"class":4866},[1736,6895,6674],{"class":4866},[1736,6897,6677],{"class":2674},[1736,6899,6680],{"class":1912},[1736,6901,6902,6904],{"class":1738,"line":2381},[1736,6903,6685],{"class":4866},[1736,6905,6688],{"class":1912},[1736,6907,6908,6910,6912,6914,6916],{"class":1738,"line":2387},[1736,6909,6693],{"class":1912},[1736,6911,6697],{"class":6696},[1736,6913,6700],{"class":2674},[1736,6915,5062],{"class":4866},[1736,6917,6705],{"class":1912},[1736,6919,6920,6922,6924,6926,6928,6930,6932,6934,6937,6940,6942],{"class":1738,"line":2393},[1736,6921,6710],{"class":1912},[1736,6923,6713],{"class":1918},[1736,6925,6700],{"class":2674},[1736,6927,5062],{"class":4866},[1736,6929,6720],{"class":1912},[1736,6931,6723],{"class":2674},[1736,6933,5062],{"class":4866},[1736,6935,6936],{"class":1912},"{src} ",[1736,6938,6939],{"class":2674},"alt",[1736,6941,5062],{"class":4866},[1736,6943,6944],{"class":1912},"{alt} />\n",[1736,6946,6947,6949,6951],{"class":1738,"line":2398},[1736,6948,6744],{"class":1912},[1736,6950,6697],{"class":6696},[1736,6952,6663],{"class":1912},[1736,6954,6955],{"class":1738,"line":2404},[1736,6956,6753],{"class":1912},[1736,6958,6960],{"class":1738,"line":6959},22,[1736,6961,1976],{"class":1912},[11,6963,6964],{},"The composition file in Bit would look something like this.",[299,6966,6968],{"className":4894,"code":6967,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { ProductDetails } from './product-details'\n\nexport const BasicProductDetails = () => (\n  \u003CProductDetails src=\"real-img-url\" alt=\"image description\" />\n)\n",[179,6969,6970,6980,6992,6996,7016,7041],{"__ignoreMap":307},[1736,6971,6972,6974,6976,6978],{"class":1738,"line":1739},[1736,6973,4996],{"class":4866},[1736,6975,6594],{"class":1912},[1736,6977,5002],{"class":4866},[1736,6979,6599],{"class":1935},[1736,6981,6982,6984,6987,6989],{"class":1738,"line":748},[1736,6983,4996],{"class":4866},[1736,6985,6986],{"class":1912}," { ProductDetails } ",[1736,6988,5002],{"class":4866},[1736,6990,6991],{"class":1935}," './product-details'\n",[1736,6993,6994],{"class":1738,"line":756},[1736,6995,1747],{"emptyLinePlaceholder":790},[1736,6997,6998,7000,7003,7006,7008,7011,7014],{"class":1738,"line":1755},[1736,6999,6632],{"class":4866},[1736,7001,7002],{"class":4866}," const",[1736,7004,7005],{"class":2674}," BasicProductDetails",[1736,7007,4911],{"class":4866},[1736,7009,7010],{"class":1912}," () ",[1736,7012,7013],{"class":4866},"=>",[1736,7015,6688],{"class":1912},[1736,7017,7018,7021,7024,7027,7029,7032,7034,7036,7039],{"class":1738,"line":1761},[1736,7019,7020],{"class":1912},"  \u003C",[1736,7022,7023],{"class":1918},"ProductDetails",[1736,7025,7026],{"class":2674}," src",[1736,7028,5062],{"class":4866},[1736,7030,7031],{"class":1935},"\"real-img-url\"",[1736,7033,6731],{"class":2674},[1736,7035,5062],{"class":4866},[1736,7037,7038],{"class":1935},"\"image description\"",[1736,7040,6739],{"class":1912},[1736,7042,7043],{"class":1738,"line":1767},[1736,7044,7045],{"class":1912},")\n",[138,7047,7049],{"id":7048},"using-mock-data","Using mock data",[11,7051,7052],{},"If you have mock data already created as a separate entity component you could just go ahead and directly use that although I prefer to import the mock data component later and make sure it is first working with some basic mock data direct in the component.",[11,7054,7055],{},"And of course we will have a lot of refactoring later with our api as really we just want to have a product and pass that as the prop rather than passing individual props. It should eventually look something more like this:",[299,7057,7059],{"className":4894,"code":7058,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { Img } from '@learn-bit-react/base-ui.ui.img'\nimport styles from './product-details.module.scss'\n\nexport type ProductDetailsProps = {\n  /**\n   * product\n   */\n  product: Shoes\n} & React.HTMLAttributes\u003CHTMLDivElement>\n\nconst {\n  product: { id, src, title, text, price, alt, rating },\n  availableColors,\n  availableSizes\n} = product\n\nexport function ProductDetails({ product }: ProductDetailsProps) {\n  return (\n    \u003Cdiv className={styles.productDetails}>\n      \u003CImg className={styles.img} src={src} alt={alt} />\n      ...\n    \u003C/div>\n  )\n}\n",[179,7060,7061,7071,7081,7091,7095,7107,7111,7116,7120,7130,7148,7152,7158,7198,7205,7210,7219,7223,7247,7253,7265,7289,7294,7303,7308],{"__ignoreMap":307},[1736,7062,7063,7065,7067,7069],{"class":1738,"line":1739},[1736,7064,4996],{"class":4866},[1736,7066,6594],{"class":1912},[1736,7068,5002],{"class":4866},[1736,7070,6599],{"class":1935},[1736,7072,7073,7075,7077,7079],{"class":1738,"line":748},[1736,7074,4996],{"class":4866},[1736,7076,6606],{"class":1912},[1736,7078,5002],{"class":4866},[1736,7080,6611],{"class":1935},[1736,7082,7083,7085,7087,7089],{"class":1738,"line":756},[1736,7084,4996],{"class":4866},[1736,7086,6618],{"class":1912},[1736,7088,5002],{"class":4866},[1736,7090,6623],{"class":1935},[1736,7092,7093],{"class":1738,"line":1755},[1736,7094,1747],{"emptyLinePlaceholder":790},[1736,7096,7097,7099,7101,7103,7105],{"class":1738,"line":1761},[1736,7098,6632],{"class":4866},[1736,7100,6635],{"class":4866},[1736,7102,6638],{"class":2674},[1736,7104,4911],{"class":4866},[1736,7106,4914],{"class":1912},[1736,7108,7109],{"class":1738,"line":1767},[1736,7110,6821],{"class":6820},[1736,7112,7113],{"class":1738,"line":1772},[1736,7114,7115],{"class":6820},"   * product\n",[1736,7117,7118],{"class":1738,"line":1778},[1736,7119,6831],{"class":6820},[1736,7121,7122,7125,7127],{"class":1738,"line":1784},[1736,7123,7124],{"class":5036},"  product",[1736,7126,1087],{"class":4866},[1736,7128,7129],{"class":2674}," Shoes\n",[1736,7131,7132,7134,7136,7138,7140,7142,7144,7146],{"class":1738,"line":1790},[1736,7133,6871],{"class":1912},[1736,7135,6646],{"class":4866},[1736,7137,6649],{"class":2674},[1736,7139,891],{"class":1912},[1736,7141,6654],{"class":2674},[1736,7143,6657],{"class":1912},[1736,7145,6660],{"class":2674},[1736,7147,6663],{"class":1912},[1736,7149,7150],{"class":1738,"line":1796},[1736,7151,1747],{"emptyLinePlaceholder":790},[1736,7153,7154,7156],{"class":1738,"line":2353},[1736,7155,5029],{"class":4866},[1736,7157,4914],{"class":1912},[1736,7159,7160,7162,7165,7168,7170,7172,7174,7177,7179,7181,7183,7186,7188,7190,7192,7195],{"class":1738,"line":2358},[1736,7161,7124],{"class":5036},[1736,7163,7164],{"class":1912},": { ",[1736,7166,7167],{"class":1918},"id",[1736,7169,829],{"class":1912},[1736,7171,6723],{"class":1918},[1736,7173,829],{"class":1912},[1736,7175,7176],{"class":1918},"title",[1736,7178,829],{"class":1912},[1736,7180,304],{"class":1918},[1736,7182,829],{"class":1912},[1736,7184,7185],{"class":1918},"price",[1736,7187,829],{"class":1912},[1736,7189,6939],{"class":1918},[1736,7191,829],{"class":1912},[1736,7193,7194],{"class":1918},"rating",[1736,7196,7197],{"class":1912}," },\n",[1736,7199,7200,7203],{"class":1738,"line":2364},[1736,7201,7202],{"class":1918},"  availableColors",[1736,7204,1939],{"class":1912},[1736,7206,7207],{"class":1738,"line":2370},[1736,7208,7209],{"class":1918},"  availableSizes\n",[1736,7211,7212,7214,7216],{"class":1738,"line":2376},[1736,7213,6871],{"class":1912},[1736,7215,5062],{"class":4866},[1736,7217,7218],{"class":1912}," product\n",[1736,7220,7221],{"class":1738,"line":2381},[1736,7222,1747],{"emptyLinePlaceholder":790},[1736,7224,7225,7227,7229,7231,7234,7237,7240,7242,7244],{"class":1738,"line":2387},[1736,7226,6632],{"class":4866},[1736,7228,6674],{"class":4866},[1736,7230,6677],{"class":2674},[1736,7232,7233],{"class":1912},"({ ",[1736,7235,7236],{"class":5036},"product",[1736,7238,7239],{"class":1912}," }",[1736,7241,1087],{"class":4866},[1736,7243,6638],{"class":2674},[1736,7245,7246],{"class":1912},") {\n",[1736,7248,7249,7251],{"class":1738,"line":2393},[1736,7250,6685],{"class":4866},[1736,7252,6688],{"class":1912},[1736,7254,7255,7257,7259,7261,7263],{"class":1738,"line":2398},[1736,7256,6693],{"class":1912},[1736,7258,6697],{"class":6696},[1736,7260,6700],{"class":2674},[1736,7262,5062],{"class":4866},[1736,7264,6705],{"class":1912},[1736,7266,7267,7269,7271,7273,7275,7277,7279,7281,7283,7285,7287],{"class":1738,"line":2404},[1736,7268,6710],{"class":1912},[1736,7270,6713],{"class":1918},[1736,7272,6700],{"class":2674},[1736,7274,5062],{"class":4866},[1736,7276,6720],{"class":1912},[1736,7278,6723],{"class":2674},[1736,7280,5062],{"class":4866},[1736,7282,6936],{"class":1912},[1736,7284,6939],{"class":2674},[1736,7286,5062],{"class":4866},[1736,7288,6944],{"class":1912},[1736,7290,7291],{"class":1738,"line":6959},[1736,7292,7293],{"class":1912},"      ...\n",[1736,7295,7297,7299,7301],{"class":1738,"line":7296},23,[1736,7298,6744],{"class":1912},[1736,7300,6697],{"class":6696},[1736,7302,6663],{"class":1912},[1736,7304,7306],{"class":1738,"line":7305},24,[1736,7307,6753],{"class":1912},[1736,7309,7311],{"class":1738,"line":7310},25,[1736,7312,1976],{"class":1912},[11,7314,7315],{},"And in the composition file we can just import our mock data and use it directly:",[299,7317,7319],{"className":4894,"code":7318,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { mockShoesHome } from '@learn-bit-react/shoe-store.entity.shoes'\nimport { ProductDetails } from './product-details'\n\nexport const ProductDetailsFromMockData = () => (\n  \u003CProductDetails product={mockShoesHome()[0]} />\n)\n",[179,7320,7321,7331,7343,7353,7357,7374,7399],{"__ignoreMap":307},[1736,7322,7323,7325,7327,7329],{"class":1738,"line":1739},[1736,7324,4996],{"class":4866},[1736,7326,6594],{"class":1912},[1736,7328,5002],{"class":4866},[1736,7330,6599],{"class":1935},[1736,7332,7333,7335,7338,7340],{"class":1738,"line":748},[1736,7334,4996],{"class":4866},[1736,7336,7337],{"class":1912}," { mockShoesHome } ",[1736,7339,5002],{"class":4866},[1736,7341,7342],{"class":1935}," '@learn-bit-react/shoe-store.entity.shoes'\n",[1736,7344,7345,7347,7349,7351],{"class":1738,"line":756},[1736,7346,4996],{"class":4866},[1736,7348,6986],{"class":1912},[1736,7350,5002],{"class":4866},[1736,7352,6991],{"class":1935},[1736,7354,7355],{"class":1738,"line":1755},[1736,7356,1747],{"emptyLinePlaceholder":790},[1736,7358,7359,7361,7363,7366,7368,7370,7372],{"class":1738,"line":1761},[1736,7360,6632],{"class":4866},[1736,7362,7002],{"class":4866},[1736,7364,7365],{"class":2674}," ProductDetailsFromMockData",[1736,7367,4911],{"class":4866},[1736,7369,7010],{"class":1912},[1736,7371,7013],{"class":4866},[1736,7373,6688],{"class":1912},[1736,7375,7376,7378,7380,7383,7385,7388,7391,7394,7396],{"class":1738,"line":1767},[1736,7377,7020],{"class":1912},[1736,7379,7023],{"class":1918},[1736,7381,7382],{"class":2674}," product",[1736,7384,5062],{"class":4866},[1736,7386,7387],{"class":1912},"{",[1736,7389,7390],{"class":2674},"mockShoesHome",[1736,7392,7393],{"class":1912},"()[",[1736,7395,1290],{"class":1918},[1736,7397,7398],{"class":1912},"]} />\n",[1736,7400,7401],{"class":1738,"line":1772},[1736,7402,7045],{"class":1912},[11,7404,7405],{},"Use whatever method works best when building the component depending on who you are working with etc.",[23,7407,7409],{"id":7408},"adding-our-second-component","Adding our Second Component",[11,7411,7412,7413,7418],{},"The next component we need is the ",[15,7414,7417],{"href":7415,"rel":7416},"https://bit.dev/learn-bit-react/base-ui/ui/heading",[19],"heading component"," to show the name of the shoe. As we already have this component we can simply install it",[6368,7420,6376],{"provider":3460,"src":7421,"format":6371,"quality":6372,"fit":6373,"alt":7422,"loading":6375},"debbie.codes/blog/heading-component_2x_abv6ps","Heading Component in Bit",[299,7424,7426],{"className":2665,"code":7425,"language":2667,"meta":307,"style":307},"bit install @learn-bit-react/base-ui.ui.heading\n",[179,7427,7428],{"__ignoreMap":307},[1736,7429,7430,7432,7434],{"class":1738,"line":1739},[1736,7431,4970],{"class":2674},[1736,7433,4973],{"class":1935},[1736,7435,7436],{"class":1935}," @learn-bit-react/base-ui.ui.heading\n",[11,7438,7439],{},"And then use it in the component.",[299,7441,7443],{"className":4894,"code":7442,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { Img } from '@learn-bit-react/base-ui.ui.img'\nimport { Heading } from '@learn-bit-react/base-ui.ui.heading'\nimport styles from './product-details.module.scss'\n\nexport type ProductDetailsProps = {} & React.HTMLAttributes\u003CHTMLDivElement>\n\nexport function ProductDetails() {\n  return (\n    \u003Cdiv className={styles.productDetails}>\n      \u003CImg className={styles.img} src=\"some-img-url\" alt=\"alt\" />\n      \u003CHeading element=\"h1\">Product Name\u003C/Heading>\n    \u003C/div>\n  )\n}\n",[179,7444,7445,7455,7465,7477,7487,7491,7517,7521,7531,7537,7549,7575,7597,7605,7609],{"__ignoreMap":307},[1736,7446,7447,7449,7451,7453],{"class":1738,"line":1739},[1736,7448,4996],{"class":4866},[1736,7450,6594],{"class":1912},[1736,7452,5002],{"class":4866},[1736,7454,6599],{"class":1935},[1736,7456,7457,7459,7461,7463],{"class":1738,"line":748},[1736,7458,4996],{"class":4866},[1736,7460,6606],{"class":1912},[1736,7462,5002],{"class":4866},[1736,7464,6611],{"class":1935},[1736,7466,7467,7469,7472,7474],{"class":1738,"line":756},[1736,7468,4996],{"class":4866},[1736,7470,7471],{"class":1912}," { Heading } ",[1736,7473,5002],{"class":4866},[1736,7475,7476],{"class":1935}," '@learn-bit-react/base-ui.ui.heading'\n",[1736,7478,7479,7481,7483,7485],{"class":1738,"line":1755},[1736,7480,4996],{"class":4866},[1736,7482,6618],{"class":1912},[1736,7484,5002],{"class":4866},[1736,7486,6623],{"class":1935},[1736,7488,7489],{"class":1738,"line":1761},[1736,7490,1747],{"emptyLinePlaceholder":790},[1736,7492,7493,7495,7497,7499,7501,7503,7505,7507,7509,7511,7513,7515],{"class":1738,"line":1767},[1736,7494,6632],{"class":4866},[1736,7496,6635],{"class":4866},[1736,7498,6638],{"class":2674},[1736,7500,4911],{"class":4866},[1736,7502,6643],{"class":1912},[1736,7504,6646],{"class":4866},[1736,7506,6649],{"class":2674},[1736,7508,891],{"class":1912},[1736,7510,6654],{"class":2674},[1736,7512,6657],{"class":1912},[1736,7514,6660],{"class":2674},[1736,7516,6663],{"class":1912},[1736,7518,7519],{"class":1738,"line":1772},[1736,7520,1747],{"emptyLinePlaceholder":790},[1736,7522,7523,7525,7527,7529],{"class":1738,"line":1778},[1736,7524,6632],{"class":4866},[1736,7526,6674],{"class":4866},[1736,7528,6677],{"class":2674},[1736,7530,6680],{"class":1912},[1736,7532,7533,7535],{"class":1738,"line":1784},[1736,7534,6685],{"class":4866},[1736,7536,6688],{"class":1912},[1736,7538,7539,7541,7543,7545,7547],{"class":1738,"line":1790},[1736,7540,6693],{"class":1912},[1736,7542,6697],{"class":6696},[1736,7544,6700],{"class":2674},[1736,7546,5062],{"class":4866},[1736,7548,6705],{"class":1912},[1736,7550,7551,7553,7555,7557,7559,7561,7563,7565,7567,7569,7571,7573],{"class":1738,"line":1796},[1736,7552,6710],{"class":1912},[1736,7554,6713],{"class":1918},[1736,7556,6700],{"class":2674},[1736,7558,5062],{"class":4866},[1736,7560,6720],{"class":1912},[1736,7562,6723],{"class":2674},[1736,7564,5062],{"class":4866},[1736,7566,6728],{"class":1935},[1736,7568,6731],{"class":2674},[1736,7570,5062],{"class":4866},[1736,7572,6736],{"class":1935},[1736,7574,6739],{"class":1912},[1736,7576,7577,7579,7582,7585,7587,7590,7593,7595],{"class":1738,"line":2353},[1736,7578,6710],{"class":1912},[1736,7580,7581],{"class":1918},"Heading",[1736,7583,7584],{"class":2674}," element",[1736,7586,5062],{"class":4866},[1736,7588,7589],{"class":1935},"\"h1\"",[1736,7591,7592],{"class":1912},">Product Name\u003C/",[1736,7594,7581],{"class":1918},[1736,7596,6663],{"class":1912},[1736,7598,7599,7601,7603],{"class":1738,"line":2358},[1736,7600,6744],{"class":1912},[1736,7602,6697],{"class":6696},[1736,7604,6663],{"class":1912},[1736,7606,7607],{"class":1738,"line":2364},[1736,7608,6753],{"class":1912},[1736,7610,7611],{"class":1738,"line":2370},[1736,7612,1976],{"class":1912},[23,7614,7616],{"id":7615},"building-one-of-the-new-components","Building one of the new components",[11,7618,7619],{},"As we see our component taking shape the next component according to our design is one we do not have. This is where the process changes. We need a rating component and there are two things we can do here. We can go build the rating component or we can put a placeholder here and come back to build it later as maybe it might take more time, we might have to do more research or perhaps another member of the team is actually building it.",[11,7621,7622],{},"Personally I used a placeholder so I could more or less get the product details component to look almost finished and then come back and work on the missing components that needed building.",[11,7624,7625,7626,7631],{},"To build the ",[15,7627,7630],{"href":7628,"rel":7629},"https://bit.dev/learn-bit-react/ecommerce/ui/product/rating",[19],"rating component"," I created the component in the ecommerce scope and started building it completely in isolation from the product details component. Once it was working how it should be, I then imported it into my product details component the same way I imported the Image and Heading component using the full package name and then used it in the product details component passing in the necessary values to the props to make it work.",[6368,7633,6376],{"provider":3460,"src":7634,"format":6371,"quality":6372,"fit":6373,"alt":7635,"loading":6375},"debbie.codes/blog/rating-component_2x_unane9","Rating Component in Bit",[23,7637,7639],{"id":7638},"the-finished-product","The Finished Product",[11,7641,7642],{},"Once we have finished going through all components we should now have a very decent looking product details component that now needs to make sure the mock data is working correctly and that it is styled as per the design. Of course documenting the component is important so others know how to use the component and what props are available. Proving a live playground makes it much more fun for others to be able to test your component out. And of course adding tests to your components are very important and should be added when possible. That is something I am still working on and trying to improve. With more testing knowledge this of course becomes much easier.",[6368,7644,6376],{"provider":3460,"src":7645,"format":6371,"quality":6372,"fit":6373,"alt":6374,"loading":6375},"debbie.codes/blog/product-details_2x_fziqbi",[23,7647,7649],{"id":7648},"dependency-graph","Dependency Graph",[11,7651,7652],{},"We now have a product details component with a dependency graph showing all the components we used as well as the name and scope of that component and its version. It is also easy to see which component depends on which such as the select-size component from the ecommerce scope which depends on the select component from the base-ui scope.",[6368,7654,6376],{"provider":3460,"src":7655,"format":6371,"quality":6372,"fit":6373,"alt":7656,"loading":6375},"debbie.codes/blog/dependency-graph-product-details_2x_d657gn","Dependency Graph for Product details component in Bit",[23,7658,7660],{"id":7659},"adding-our-component-to-our-page","Adding our component to our page",[11,7662,7663],{},"Once the product details component is finished we can now import it into our product page and make sure it works at app level. There are a few extra things we have to do here such as apply route params to make sure we are showing the right product depending on the route.",[299,7665,7667],{"className":4894,"code":7666,"language":4896,"meta":307,"style":307},"import React from 'react';\nimport { ProductDetails } from '@learn-bit-react/ecommerce.ui.product.product-details';\nimport { useParams } from 'react-router-dom';\nimport { allShoes } from '@learn-bit-react/shoe-store.entity.shoes';\nimport styles from './product.module.scss';\n\nfunction getShoe(shoeId: string) {\n  return allShoes.find(({ product }) => shoeId === product.id) || allShoes[0];\n}\n\nexport function Product() {\n  const { id } = useParams() as { id: string };\n  const shoe = getShoe(id);\n\n  return (\n    \u003Cdiv className={styles.product}>\n      \u003CProductDetails product={shoe} />\n      \u003Ch2>reviews component\u003C/h2>\n      \u003Ch2>featured-products component\u003C/h2>\n      \u003Ch3>banner component\u003C/h3>\n    \u003C/div>\n  );\n}\n",[179,7668,7669,7683,7696,7710,7724,7737,7741,7761,7801,7805,7809,7820,7855,7869,7873,7879,7892,7905,7918,7931,7944,7952,7957],{"__ignoreMap":307},[1736,7670,7671,7673,7675,7677,7680],{"class":1738,"line":1739},[1736,7672,4996],{"class":4866},[1736,7674,6594],{"class":1912},[1736,7676,5002],{"class":4866},[1736,7678,7679],{"class":1935}," 'react'",[1736,7681,7682],{"class":1912},";\n",[1736,7684,7685,7687,7689,7691,7694],{"class":1738,"line":748},[1736,7686,4996],{"class":4866},[1736,7688,6986],{"class":1912},[1736,7690,5002],{"class":4866},[1736,7692,7693],{"class":1935}," '@learn-bit-react/ecommerce.ui.product.product-details'",[1736,7695,7682],{"class":1912},[1736,7697,7698,7700,7703,7705,7708],{"class":1738,"line":756},[1736,7699,4996],{"class":4866},[1736,7701,7702],{"class":1912}," { useParams } ",[1736,7704,5002],{"class":4866},[1736,7706,7707],{"class":1935}," 'react-router-dom'",[1736,7709,7682],{"class":1912},[1736,7711,7712,7714,7717,7719,7722],{"class":1738,"line":1755},[1736,7713,4996],{"class":4866},[1736,7715,7716],{"class":1912}," { allShoes } ",[1736,7718,5002],{"class":4866},[1736,7720,7721],{"class":1935}," '@learn-bit-react/shoe-store.entity.shoes'",[1736,7723,7682],{"class":1912},[1736,7725,7726,7728,7730,7732,7735],{"class":1738,"line":1761},[1736,7727,4996],{"class":4866},[1736,7729,6618],{"class":1912},[1736,7731,5002],{"class":4866},[1736,7733,7734],{"class":1935}," './product.module.scss'",[1736,7736,7682],{"class":1912},[1736,7738,7739],{"class":1738,"line":1767},[1736,7740,1747],{"emptyLinePlaceholder":790},[1736,7742,7743,7746,7749,7752,7755,7757,7759],{"class":1738,"line":1772},[1736,7744,7745],{"class":4866},"function",[1736,7747,7748],{"class":2674}," getShoe",[1736,7750,7751],{"class":1912},"(",[1736,7753,7754],{"class":5036},"shoeId",[1736,7756,1087],{"class":4866},[1736,7758,6841],{"class":1918},[1736,7760,7246],{"class":1912},[1736,7762,7763,7765,7768,7771,7774,7776,7779,7781,7784,7787,7790,7793,7796,7798],{"class":1738,"line":1778},[1736,7764,6685],{"class":4866},[1736,7766,7767],{"class":1912}," allShoes.",[1736,7769,7770],{"class":2674},"find",[1736,7772,7773],{"class":1912},"(({ ",[1736,7775,7236],{"class":5036},[1736,7777,7778],{"class":1912}," }) ",[1736,7780,7013],{"class":4866},[1736,7782,7783],{"class":1912}," shoeId ",[1736,7785,7786],{"class":4866},"===",[1736,7788,7789],{"class":1912}," product.id) ",[1736,7791,7792],{"class":4866},"||",[1736,7794,7795],{"class":1912}," allShoes[",[1736,7797,1290],{"class":1918},[1736,7799,7800],{"class":1912},"];\n",[1736,7802,7803],{"class":1738,"line":1784},[1736,7804,1976],{"class":1912},[1736,7806,7807],{"class":1738,"line":1790},[1736,7808,1747],{"emptyLinePlaceholder":790},[1736,7810,7811,7813,7815,7818],{"class":1738,"line":1796},[1736,7812,6632],{"class":4866},[1736,7814,6674],{"class":4866},[1736,7816,7817],{"class":2674}," Product",[1736,7819,6680],{"class":1912},[1736,7821,7822,7825,7828,7830,7833,7835,7838,7841,7844,7846,7848,7850,7852],{"class":1738,"line":2353},[1736,7823,7824],{"class":4866},"  const",[1736,7826,7827],{"class":1912}," { ",[1736,7829,7167],{"class":1918},[1736,7831,7832],{"class":1912}," } ",[1736,7834,5062],{"class":4866},[1736,7836,7837],{"class":2674}," useParams",[1736,7839,7840],{"class":1912},"() ",[1736,7842,7843],{"class":4866},"as",[1736,7845,7827],{"class":1912},[1736,7847,7167],{"class":5036},[1736,7849,1087],{"class":4866},[1736,7851,6841],{"class":1918},[1736,7853,7854],{"class":1912}," };\n",[1736,7856,7857,7859,7862,7864,7866],{"class":1738,"line":2358},[1736,7858,7824],{"class":4866},[1736,7860,7861],{"class":1918}," shoe",[1736,7863,4911],{"class":4866},[1736,7865,7748],{"class":2674},[1736,7867,7868],{"class":1912},"(id);\n",[1736,7870,7871],{"class":1738,"line":2364},[1736,7872,1747],{"emptyLinePlaceholder":790},[1736,7874,7875,7877],{"class":1738,"line":2370},[1736,7876,6685],{"class":4866},[1736,7878,6688],{"class":1912},[1736,7880,7881,7883,7885,7887,7889],{"class":1738,"line":2376},[1736,7882,6693],{"class":1912},[1736,7884,6697],{"class":6696},[1736,7886,6700],{"class":2674},[1736,7888,5062],{"class":4866},[1736,7890,7891],{"class":1912},"{styles.product}>\n",[1736,7893,7894,7896,7898,7900,7902],{"class":1738,"line":2381},[1736,7895,6710],{"class":1912},[1736,7897,7023],{"class":1918},[1736,7899,7382],{"class":2674},[1736,7901,5062],{"class":4866},[1736,7903,7904],{"class":1912},"{shoe} />\n",[1736,7906,7907,7909,7911,7914,7916],{"class":1738,"line":2387},[1736,7908,6710],{"class":1912},[1736,7910,23],{"class":6696},[1736,7912,7913],{"class":1912},">reviews component\u003C/",[1736,7915,23],{"class":6696},[1736,7917,6663],{"class":1912},[1736,7919,7920,7922,7924,7927,7929],{"class":1738,"line":2393},[1736,7921,6710],{"class":1912},[1736,7923,23],{"class":6696},[1736,7925,7926],{"class":1912},">featured-products component\u003C/",[1736,7928,23],{"class":6696},[1736,7930,6663],{"class":1912},[1736,7932,7933,7935,7937,7940,7942],{"class":1738,"line":2398},[1736,7934,6710],{"class":1912},[1736,7936,138],{"class":6696},[1736,7938,7939],{"class":1912},">banner component\u003C/",[1736,7941,138],{"class":6696},[1736,7943,6663],{"class":1912},[1736,7945,7946,7948,7950],{"class":1738,"line":2404},[1736,7947,6744],{"class":1912},[1736,7949,6697],{"class":6696},[1736,7951,6663],{"class":1912},[1736,7953,7954],{"class":1738,"line":6959},[1736,7955,7956],{"class":1912},"  );\n",[1736,7958,7959],{"class":1738,"line":7296},[1736,7960,1976],{"class":1912},[11,7962,7963],{},"First we need to add our new route to our app with a dynamic id. In the app I use the React Router Routing Provider component that has been created by the Bit team.",[299,7965,7967],{"className":2665,"code":7966,"language":2667,"meta":307,"style":307},"bit install @teambit/ui-foundation.ui.navigation.react-router.routing-adapter\n",[179,7968,7969],{"__ignoreMap":307},[1736,7970,7971,7973,7975],{"class":1738,"line":1739},[1736,7972,4970],{"class":2674},[1736,7974,4973],{"class":1935},[1736,7976,7977],{"class":1935}," @teambit/ui-foundation.ui.navigation.react-router.routing-adapter\n",[11,7979,7980],{},"I then import the component and wrap my routes in the routing Provider component.",[299,7982,7984],{"className":4894,"code":7983,"language":4896,"meta":307,"style":307},"...\nimport { ReactRouterRoutingProvider } from '@teambit/ui-foundation.ui.navigation.react-router.routing-adapter';\nimport { Product } from '@learn-bit-react/shoe-store.ui.pages.product';\n...\nexport function ShoeStoreApp() {\n...\n\u003CReactRouterRoutingProvider useBrowserRouter>\n  \u003CSwitch>\n    \u003CRoute path=\"/product/:id\">\n      \u003CProduct />\n    \u003C/Route>\n  \u003C/Switch>\n\u003C/ReactRouterRoutingProvider>\n...\n",[179,7985,7986,7990,8004,8018,8022,8033,8037,8049,8058,8075,8084,8092,8101,8110],{"__ignoreMap":307},[1736,7987,7988],{"class":1738,"line":1739},[1736,7989,5073],{"class":4866},[1736,7991,7992,7994,7997,7999,8002],{"class":1738,"line":748},[1736,7993,4996],{"class":4866},[1736,7995,7996],{"class":1912}," { ReactRouterRoutingProvider } ",[1736,7998,5002],{"class":4866},[1736,8000,8001],{"class":1935}," '@teambit/ui-foundation.ui.navigation.react-router.routing-adapter'",[1736,8003,7682],{"class":1912},[1736,8005,8006,8008,8011,8013,8016],{"class":1738,"line":756},[1736,8007,4996],{"class":4866},[1736,8009,8010],{"class":1912}," { Product } ",[1736,8012,5002],{"class":4866},[1736,8014,8015],{"class":1935}," '@learn-bit-react/shoe-store.ui.pages.product'",[1736,8017,7682],{"class":1912},[1736,8019,8020],{"class":1738,"line":1755},[1736,8021,5073],{"class":4866},[1736,8023,8024,8026,8028,8031],{"class":1738,"line":1761},[1736,8025,6632],{"class":4866},[1736,8027,6674],{"class":4866},[1736,8029,8030],{"class":2674}," ShoeStoreApp",[1736,8032,6680],{"class":1912},[1736,8034,8035],{"class":1738,"line":1767},[1736,8036,5073],{"class":4866},[1736,8038,8039,8041,8044,8047],{"class":1738,"line":1772},[1736,8040,6657],{"class":1912},[1736,8042,8043],{"class":1918},"ReactRouterRoutingProvider",[1736,8045,8046],{"class":2674}," useBrowserRouter",[1736,8048,6663],{"class":1912},[1736,8050,8051,8053,8056],{"class":1738,"line":1778},[1736,8052,7020],{"class":1912},[1736,8054,8055],{"class":1918},"Switch",[1736,8057,6663],{"class":1912},[1736,8059,8060,8062,8065,8068,8070,8073],{"class":1738,"line":1784},[1736,8061,6693],{"class":1912},[1736,8063,8064],{"class":1918},"Route",[1736,8066,8067],{"class":2674}," path",[1736,8069,5062],{"class":4866},[1736,8071,8072],{"class":1935},"\"/product/:id\"",[1736,8074,6663],{"class":1912},[1736,8076,8077,8079,8082],{"class":1738,"line":1790},[1736,8078,6710],{"class":1912},[1736,8080,8081],{"class":1918},"Product",[1736,8083,6739],{"class":1912},[1736,8085,8086,8088,8090],{"class":1738,"line":1796},[1736,8087,6744],{"class":1912},[1736,8089,8064],{"class":1918},[1736,8091,6663],{"class":1912},[1736,8093,8094,8097,8099],{"class":1738,"line":2353},[1736,8095,8096],{"class":1912},"  \u003C/",[1736,8098,8055],{"class":1918},[1736,8100,6663],{"class":1912},[1736,8102,8103,8106,8108],{"class":1738,"line":2358},[1736,8104,8105],{"class":1912},"\u003C/",[1736,8107,8043],{"class":1918},[1736,8109,6663],{"class":1912},[1736,8111,8112],{"class":1738,"line":2364},[1736,8113,5073],{"class":4866},[23,8115,8117],{"id":8116},"routing-in-bit-compositions","Routing in Bit Compositions",[11,8119,8120],{},"When working with Bit we need to take a few things into account when working on compositions. As we don't have a browser router we don't have access to params and therefore this page and the app will not work at composition level.",[11,8122,8123],{},"To fix this we need to work with the memory router from react router which we can import and then wrap our composition in it. We also need to add a routing provider. These components have already been created by teambit so we can go ahead and install them and then use them directly. This will allow our Link component from our product card to correctly link to the right product.",[11,8125,8126],{},"Our Link component uses the react router navigation link from teambit",[299,8128,8130],{"className":4894,"code":8129,"language":4896,"meta":307,"style":307},"import { Link as BaseLink } from '@teambit/base-react.navigation.link'\n\nimport React from 'react'\nimport { MemoryRouter } from 'react-router-dom'\nimport { ReactRouterRoutingProvider } from '@teambit/ui-foundation.ui.navigation.react-router.routing-adapter'\nimport { ShoeStoreApp } from './app'\n\nexport const ShoeStoreBasic = () => {\n  return (\n    \u003CMemoryRouter>\n      \u003CReactRouterRoutingProvider>\n        \u003CShoeStoreApp>\u003C/ShoeStoreApp>\n      \u003C/ReactRouterRoutingProvider>\n    \u003C/MemoryRouter>\n  )\n}\n",[179,8131,8132,8149,8153,8163,8175,8186,8198,8202,8219,8225,8234,8242,8257,8266,8274,8278],{"__ignoreMap":307},[1736,8133,8134,8136,8139,8141,8144,8146],{"class":1738,"line":1739},[1736,8135,4996],{"class":4866},[1736,8137,8138],{"class":1912}," { Link ",[1736,8140,7843],{"class":4866},[1736,8142,8143],{"class":1912}," BaseLink } ",[1736,8145,5002],{"class":4866},[1736,8147,8148],{"class":1935}," '@teambit/base-react.navigation.link'\n",[1736,8150,8151],{"class":1738,"line":748},[1736,8152,1747],{"emptyLinePlaceholder":790},[1736,8154,8155,8157,8159,8161],{"class":1738,"line":756},[1736,8156,4996],{"class":4866},[1736,8158,6594],{"class":1912},[1736,8160,5002],{"class":4866},[1736,8162,6599],{"class":1935},[1736,8164,8165,8167,8170,8172],{"class":1738,"line":1755},[1736,8166,4996],{"class":4866},[1736,8168,8169],{"class":1912}," { MemoryRouter } ",[1736,8171,5002],{"class":4866},[1736,8173,8174],{"class":1935}," 'react-router-dom'\n",[1736,8176,8177,8179,8181,8183],{"class":1738,"line":1761},[1736,8178,4996],{"class":4866},[1736,8180,7996],{"class":1912},[1736,8182,5002],{"class":4866},[1736,8184,8185],{"class":1935}," '@teambit/ui-foundation.ui.navigation.react-router.routing-adapter'\n",[1736,8187,8188,8190,8193,8195],{"class":1738,"line":1767},[1736,8189,4996],{"class":4866},[1736,8191,8192],{"class":1912}," { ShoeStoreApp } ",[1736,8194,5002],{"class":4866},[1736,8196,8197],{"class":1935}," './app'\n",[1736,8199,8200],{"class":1738,"line":1772},[1736,8201,1747],{"emptyLinePlaceholder":790},[1736,8203,8204,8206,8208,8211,8213,8215,8217],{"class":1738,"line":1778},[1736,8205,6632],{"class":4866},[1736,8207,7002],{"class":4866},[1736,8209,8210],{"class":2674}," ShoeStoreBasic",[1736,8212,4911],{"class":4866},[1736,8214,7010],{"class":1912},[1736,8216,7013],{"class":4866},[1736,8218,4914],{"class":1912},[1736,8220,8221,8223],{"class":1738,"line":1784},[1736,8222,6685],{"class":4866},[1736,8224,6688],{"class":1912},[1736,8226,8227,8229,8232],{"class":1738,"line":1790},[1736,8228,6693],{"class":1912},[1736,8230,8231],{"class":1918},"MemoryRouter",[1736,8233,6663],{"class":1912},[1736,8235,8236,8238,8240],{"class":1738,"line":1796},[1736,8237,6710],{"class":1912},[1736,8239,8043],{"class":1918},[1736,8241,6663],{"class":1912},[1736,8243,8244,8247,8250,8253,8255],{"class":1738,"line":2353},[1736,8245,8246],{"class":1912},"        \u003C",[1736,8248,8249],{"class":1918},"ShoeStoreApp",[1736,8251,8252],{"class":1912},">\u003C/",[1736,8254,8249],{"class":1918},[1736,8256,6663],{"class":1912},[1736,8258,8259,8262,8264],{"class":1738,"line":2358},[1736,8260,8261],{"class":1912},"      \u003C/",[1736,8263,8043],{"class":1918},[1736,8265,6663],{"class":1912},[1736,8267,8268,8270,8272],{"class":1738,"line":2364},[1736,8269,6744],{"class":1912},[1736,8271,8231],{"class":1918},[1736,8273,6663],{"class":1912},[1736,8275,8276],{"class":1738,"line":2370},[1736,8277,6753],{"class":1912},[1736,8279,8280],{"class":1738,"line":2376},[1736,8281,1976],{"class":1912},[11,8283,8284],{},"As this component is built with ESM there are a few things we need to do in order for it to work until we have better ESM support.",[11,8286,8287],{},"In our environment component's runtime file we have to sure we are overriding the Jest config with our custom jest config.",[299,8289,8291],{"className":4894,"code":8290,"language":4896,"meta":307,"style":307},"react.overrideJestConfig(require.resolve('./jest/jest.config')),\n",[179,8292,8293],{"__ignoreMap":307},[1736,8294,8295,8298,8301,8304,8307,8309,8312],{"class":1738,"line":1739},[1736,8296,8297],{"class":1912},"react.",[1736,8299,8300],{"class":2674},"overrideJestConfig",[1736,8302,8303],{"class":1912},"(require.",[1736,8305,8306],{"class":2674},"resolve",[1736,8308,7751],{"class":1912},[1736,8310,8311],{"class":1935},"'./jest/jest.config'",[1736,8313,8314],{"class":1912},")),\n",[11,8316,8317],{},"The custom Jest config should then ignore the components it needs to ignore. As we are requiring the packages excluder we will need to install it so we can use it",[299,8319,8321],{"className":2665,"code":8320,"language":2667,"meta":307,"style":307},"bit install @teambit/dependencies.modules.packages-excluder\n",[179,8322,8323],{"__ignoreMap":307},[1736,8324,8325,8327,8329],{"class":1738,"line":1739},[1736,8326,4970],{"class":2674},[1736,8328,4973],{"class":1935},[1736,8330,8331],{"class":1935}," @teambit/dependencies.modules.packages-excluder\n",[11,8333,8334],{},"We then exclude the packages we want to exclude which in our case is our link component and all teambits components.",[299,8336,8338],{"className":4894,"code":8337,"language":4896,"meta":307,"style":307},"// Override the Jest config to ignore transpiling from specific folders\n\nconst reactJestConfig = require('@teambit/react/jest/jest.config')\nconst {\n  generateNodeModulesPattern\n} = require('@teambit/dependencies.modules.packages-excluder')\n\nconst packagesToExclude = ['@learn-bit-react/base-ui.ui.link', '@teambit']\n\nmodule.exports = {\n  ...reactJestConfig,\n  transformIgnorePatterns: [\n    '^.+\\\\.module\\\\.(css|sass|scss)$',\n    generateNodeModulesPattern({ packages: packagesToExclude })\n  ]\n}\n",[179,8339,8340,8345,8349,8368,8374,8379,8394,8398,8421,8425,8437,8445,8450,8468,8476,8481],{"__ignoreMap":307},[1736,8341,8342],{"class":1738,"line":1739},[1736,8343,8344],{"class":6820},"// Override the Jest config to ignore transpiling from specific folders\n",[1736,8346,8347],{"class":1738,"line":748},[1736,8348,1747],{"emptyLinePlaceholder":790},[1736,8350,8351,8353,8356,8358,8361,8363,8366],{"class":1738,"line":756},[1736,8352,5029],{"class":4866},[1736,8354,8355],{"class":1918}," reactJestConfig",[1736,8357,4911],{"class":4866},[1736,8359,8360],{"class":2674}," require",[1736,8362,7751],{"class":1912},[1736,8364,8365],{"class":1935},"'@teambit/react/jest/jest.config'",[1736,8367,7045],{"class":1912},[1736,8369,8370,8372],{"class":1738,"line":1755},[1736,8371,5029],{"class":4866},[1736,8373,4914],{"class":1912},[1736,8375,8376],{"class":1738,"line":1761},[1736,8377,8378],{"class":1918},"  generateNodeModulesPattern\n",[1736,8380,8381,8383,8385,8387,8389,8392],{"class":1738,"line":1767},[1736,8382,6871],{"class":1912},[1736,8384,5062],{"class":4866},[1736,8386,8360],{"class":2674},[1736,8388,7751],{"class":1912},[1736,8390,8391],{"class":1935},"'@teambit/dependencies.modules.packages-excluder'",[1736,8393,7045],{"class":1912},[1736,8395,8396],{"class":1738,"line":1772},[1736,8397,1747],{"emptyLinePlaceholder":790},[1736,8399,8400,8402,8405,8407,8410,8413,8415,8418],{"class":1738,"line":1778},[1736,8401,5029],{"class":4866},[1736,8403,8404],{"class":1918}," packagesToExclude",[1736,8406,4911],{"class":4866},[1736,8408,8409],{"class":1912}," [",[1736,8411,8412],{"class":1935},"'@learn-bit-react/base-ui.ui.link'",[1736,8414,829],{"class":1912},[1736,8416,8417],{"class":1935},"'@teambit'",[1736,8419,8420],{"class":1912},"]\n",[1736,8422,8423],{"class":1738,"line":1784},[1736,8424,1747],{"emptyLinePlaceholder":790},[1736,8426,8427,8429,8431,8433,8435],{"class":1738,"line":1790},[1736,8428,4903],{"class":1918},[1736,8430,891],{"class":1912},[1736,8432,4908],{"class":1918},[1736,8434,4911],{"class":4866},[1736,8436,4914],{"class":1912},[1736,8438,8439,8442],{"class":1738,"line":1796},[1736,8440,8441],{"class":4866},"  ...",[1736,8443,8444],{"class":1912},"reactJestConfig,\n",[1736,8446,8447],{"class":1738,"line":2353},[1736,8448,8449],{"class":1912},"  transformIgnorePatterns: [\n",[1736,8451,8452,8455,8458,8461,8463,8466],{"class":1738,"line":2358},[1736,8453,8454],{"class":1935},"    '^.+",[1736,8456,8457],{"class":1918},"\\\\",[1736,8459,8460],{"class":1935},".module",[1736,8462,8457],{"class":1918},[1736,8464,8465],{"class":1935},".(css|sass|scss)$'",[1736,8467,1939],{"class":1912},[1736,8469,8470,8473],{"class":1738,"line":2364},[1736,8471,8472],{"class":2674},"    generateNodeModulesPattern",[1736,8474,8475],{"class":1912},"({ packages: packagesToExclude })\n",[1736,8477,8478],{"class":1738,"line":2370},[1736,8479,8480],{"class":1912},"  ]\n",[1736,8482,8483],{"class":1738,"line":2376},[1736,8484,1976],{"class":1912},[11,8486,8487],{},"We can now use this component just like any other but we will not be able to write any tests for the link component as the test files will be ignored by Jest and it will just show up as if we have no tests even if you write them.",[23,8489,3294],{"id":3293},[11,8491,8492],{},"We can then continue to build the next component of this page which is the reviews component following the same workflow as we did above and so on until we have the page fully complete.",[6368,8494,6376],{"provider":3460,"src":8495,"format":6371,"quality":6372,"fit":6373,"alt":8496,"loading":6375},"debbie.codes/blog/shoe-store-app_2x_xbftk9","Shoe store app in Bit",[11,8498,8499],{},"Of course our work is still not done here. We have only added the design component for the add to cart button and at the moment this is not going to add anything to a cart. For now it looks good and can pass the design inspection while we work on the next stage of the project. The cart page including the add to cart functionality.",[23,8501,8503],{"id":8502},"after-thoughts","After thoughts",[11,8505,8506],{},"Once I had built the product details page I then thought about what if I was to use this in the perfume store. The perfume store has no colors or sizes and therefore doesn't need these components. In previous times I would have added an if else clause to this component so that if it received the data of color then render the available colors component else don't render it.",[11,8508,8509],{},"But by doing this we are going into the building one component to suit many situations. Really we should look at the product component as something related to all products such as name, description, price etc and then add children so that we can build a shoe details component that uses the product details component but expands on it by adding in the shore specific components such as available size and color.",[11,8511,8512],{},"Again another way of doing this is to create a completely new component of shoe details and not use anything from the ecommerce which means everything can be positioned completely differently form the product details. This is of course building incase one day we want to change things and is probably not necessary at all so for now I prefer to go with the first option of extending the product details component by adding in the shoe specific components only.",[23,8514,8516],{"id":8515},"what-it-has-been-like-to-build-this-in-bit","What it has been like to build this in Bit",[11,8518,8519],{},"We are all used to building things inside our application kinda like a monorepo where we have access to all components all the time. When working with Bit we are working in one workspace. I used the shoe store workspace. But I needed to build components that are part of a different scope, part of a different repo. When creating new components I can choose to which scope they belong to and when exporting them I can add a default scope to the variants section of the workspace for each directory.",[299,8521,8523],{"className":4894,"code":8522,"language":4896,"meta":307,"style":307},"\"teambit.workspace/variants\": {\n    \"ecommerce\": {\n      \"defaultScope\": \"learn-bit-react.ecommerce\"\n    },\n   \"base-ui\": {\n      \"defaultScope\": \"learn-bit-react.base-ui\"\n   },\n}\n",[179,8524,8525,8532,8539,8549,8554,8561,8570,8575],{"__ignoreMap":307},[1736,8526,8527,8530],{"class":1738,"line":1739},[1736,8528,8529],{"class":1935},"\"teambit.workspace/variants\"",[1736,8531,1922],{"class":1912},[1736,8533,8534,8537],{"class":1738,"line":748},[1736,8535,8536],{"class":1935},"    \"ecommerce\"",[1736,8538,1922],{"class":1912},[1736,8540,8541,8544,8546],{"class":1738,"line":756},[1736,8542,8543],{"class":1935},"      \"defaultScope\"",[1736,8545,3065],{"class":1912},[1736,8547,8548],{"class":1935},"\"learn-bit-react.ecommerce\"\n",[1736,8550,8551],{"class":1738,"line":1755},[1736,8552,8553],{"class":1912},"    },\n",[1736,8555,8556,8559],{"class":1738,"line":1761},[1736,8557,8558],{"class":1935},"   \"base-ui\"",[1736,8560,1922],{"class":1912},[1736,8562,8563,8565,8567],{"class":1738,"line":1767},[1736,8564,8543],{"class":1935},[1736,8566,3065],{"class":1912},[1736,8568,8569],{"class":1935},"\"learn-bit-react.base-ui\"\n",[1736,8571,8572],{"class":1738,"line":1772},[1736,8573,8574],{"class":1912},"   },\n",[1736,8576,8577],{"class":1738,"line":1778},[1736,8578,1976],{"class":1912},[11,8580,8581],{},"That means I can now build in one workspace yet also build components that belong to and will be exported to another scope. This means that if I don't have enough people on my team I might end up having to manage more than one scope which is perfectly fine and so this gives me a better developer experience. When we import Bit components into other components we always use the full package name which means it is irrelevant if this component is in this workspace or not. The main thing we can't change is the scope name which is used as part of the package name so therefore we need to think about names from the start.",[11,8583,8584],{},"With Bit there is a cool feature called import which you can use to import any component into your workspace from another workspace should you want to modify them. This might be the case when wanting to improve the API of a component or even just a bug fix. For example when building the available colors component I needed to make changes to it so that it would work with state and being able to just import it into my workspace and work on it without having to clone another repo, tag it and then export it made such a difference.",[6368,8586,6376],{"provider":3460,"src":8587,"format":6371,"quality":6372,"fit":6373,"alt":8588,"loading":6375},"debbie.codes/blog/import-component-in-bit_2x_a9p1qs","Import component box in Bit",[11,8590,8591],{},"So what about your GitHub repo? GitHub is important for me to be able to go back in time incase something isn't working that was working before. While building I pushed all components into the shoe store repo and once I have finished I can use the eject command from bit to eject the components that I do not want in this workspace and repo.",[11,8593,8594],{},"When someone wants to work on the ecommerce repo all they have to do is run the bit import command and they will get the new imported version of the available colors component that I already tagged and versioned. If there is any merge conflicts then they can be resolved just like you would with git. This workflow was different to how I would normally work. However once I saw that components were being exported to the right scope and could easily be imported to the correct workspace should another team member need to work on it, it really did feel like a nice way to work. I was able to build things quickly and not have to worry about cloning or linking.",[11,8596,8597],{},"And of course tagging becomes easy with Bit as it auto tags any dependents. Here by changing the product component the shoe store which uses the product page component also gets a new version, once of course all tests are passing and then running the export command will export both of these components. This means I can easily tag and export components without having to worry about updating components that use the changed component.",[6368,8599,6376],{"provider":3460,"src":8600,"format":6371,"quality":8601,"fit":6373,"alt":8602,"loading":6375},"debbie.codes/blog/CleanShot_2021-12-15_at_14.59.47_2x_jrfex8","90","terminal showing tagging of components",[23,8604,8606],{"id":8605},"demo-project","Demo Project",[11,8608,8609],{},"If you want to check out the demo project then here are some links:",[70,8611,8612,8619,8626,8633,8640],{},[73,8613,8614],{},[15,8615,8618],{"href":8616,"rel":8617},"https://github.com/bit-demos/ecommerce",[19],"GitHub repo for ecommerce components",[73,8620,8621],{},[15,8622,8625],{"href":8623,"rel":8624},"https://bit.dev/learn-bit-react/ecommerce/ui/product/product-details",[19],"Product Details component on Bit",[73,8627,8628],{},[15,8629,8632],{"href":8630,"rel":8631},"https://bit.dev/learn-bit-react/base-ui",[19],"Base ui components on Bit",[73,8634,8635],{},[15,8636,8639],{"href":8637,"rel":8638},"https://bit.dev/learn-bit-react/shoe-store",[19],"Shoe store app and components on Bit",[73,8641,8642],{},[15,8643,8646],{"href":8644,"rel":8645},"https://bit-shoe-store.netlify.app",[19],"Shoe store app deployed on Netlify",[2011,8648,8649],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":307,"searchDepth":748,"depth":748,"links":8651},[8652,8655,8664,8665,8666,8667,8668,8669,8670,8671,8672,8673],{"id":6365,"depth":748,"text":6366,"children":8653},[8654],{"id":6382,"depth":756,"text":6383},{"id":6403,"depth":748,"text":6404,"children":8656},[8657,8658,8659,8660,8661,8662,8663],{"id":6410,"depth":756,"text":6411},{"id":6417,"depth":756,"text":6418},{"id":6433,"depth":756,"text":6434},{"id":6510,"depth":756,"text":6511},{"id":6520,"depth":756,"text":6521},{"id":6760,"depth":756,"text":6761},{"id":7048,"depth":756,"text":7049},{"id":7408,"depth":748,"text":7409},{"id":7615,"depth":748,"text":7616},{"id":7638,"depth":748,"text":7639},{"id":7648,"depth":748,"text":7649},{"id":7659,"depth":748,"text":7660},{"id":8116,"depth":748,"text":8117},{"id":3293,"depth":748,"text":3294},{"id":8502,"depth":748,"text":8503},{"id":8515,"depth":748,"text":8516},{"id":8605,"depth":748,"text":8606},"2021-12-23","Where do you start when building a product page for an e-commerce store? How do you think about naming the components and how much to break down the components? What do you start building first and why?","v1640267556/debbie.codes/blog/product-details-component_2x_isk5aj",{},"/blog/building-an-ecommerce-product-page",{"title":6354,"description":8675},"blog/building-an-ecommerce-product-page",[5221],"AJ-iKVRciT4mYL9_J5uZbYgXNVlYwNc5GHmQH0O7ds8",{"id":8684,"title":8685,"body":8686,"canonical":788,"date":11415,"description":11416,"extension":786,"featured":787,"image":11417,"meta":11418,"navigation":790,"ogimage":788,"path":11419,"provider":3460,"published":787,"seo":11420,"stem":11421,"tags":11422,"url":788,"__hash__":11423},"blog/blog/building-and-testing-counter-component.md","Building and Testing a Counter Component",{"type":8,"value":8687,"toc":11397},[8688,8708,8712,8717,8721,8732,8785,8789,8792,8963,8967,8995,9014,9024,9240,9253,9257,9286,9302,9306,9366,9370,9385,9441,9445,9459,9496,9500,9503,10125,10129,10132,10145,10331,10335,10338,10363,10414,10417,10488,10508,10583,10599,10718,10721,10758,10764,10827,10836,10904,10907,10966,10969,10973,11342,11344,11347,11353,11356,11358,11394],[11,8689,8690,8691,8696,8697,8701,8702,8707],{},"We want to build and test a ",[15,8692,8695],{"href":8693,"rel":8694},"https://bit.dev/learn-bit-react/ecommerce/ui/product/counter/~code/counter.tsx",[19],"counter component"," built with React and TypeScript. So where do we start? First of all looking at the component I can see it is made up of 2 ",[15,8698,8700],{"href":5197,"rel":8699},[19],"button components"," that increase and decrease the count and an ",[15,8703,8706],{"href":8704,"rel":8705},"https://bit.dev/learn-bit-react/base-ui/ui/forms/input",[19],"input component"," that shows the value of the count.",[23,8709,8711],{"id":8710},"building-a-counter-component","Building a Counter Component",[11,8713,8714],{},[121,8715],{"alt":8695,"src":8716},"https://res.cloudinary.com/debsobrien/image/upload/v1642010879/debbie.codes/blog/2022/counter_2x_ma1ik6",[138,8718,8720],{"id":8719},"importing-our-components","Importing our Components",[11,8722,8723,8724,608,8728,8731],{},"As we already have a ",[15,8725,8727],{"href":5197,"rel":8726},[19],"button component",[15,8729,8706],{"href":8704,"rel":8730},[19]," already created I will just import them as well as import React and useState and the styles.",[299,8733,8737],{"className":8734,"code":8735,"language":8736,"meta":307,"style":307},"language-js shiki shiki-themes github-light github-dark","import React, { useState } from 'react'\nimport { Button } from '@learn-bit-react/base-ui.ui.button'\nimport { Input } from '@learn-bit-react/base-ui.ui.forms.input'\nimport styles from './counter.module.scss'\n","js",[179,8738,8739,8750,8762,8774],{"__ignoreMap":307},[1736,8740,8741,8743,8746,8748],{"class":1738,"line":1739},[1736,8742,4996],{"class":4866},[1736,8744,8745],{"class":1912}," React, { useState } ",[1736,8747,5002],{"class":4866},[1736,8749,6599],{"class":1935},[1736,8751,8752,8754,8757,8759],{"class":1738,"line":748},[1736,8753,4996],{"class":4866},[1736,8755,8756],{"class":1912}," { Button } ",[1736,8758,5002],{"class":4866},[1736,8760,8761],{"class":1935}," '@learn-bit-react/base-ui.ui.button'\n",[1736,8763,8764,8766,8769,8771],{"class":1738,"line":756},[1736,8765,4996],{"class":4866},[1736,8767,8768],{"class":1912}," { Input } ",[1736,8770,5002],{"class":4866},[1736,8772,8773],{"class":1935}," '@learn-bit-react/base-ui.ui.forms.input'\n",[1736,8775,8776,8778,8780,8782],{"class":1738,"line":1755},[1736,8777,4996],{"class":4866},[1736,8779,6618],{"class":1912},[1736,8781,5002],{"class":4866},[1736,8783,8784],{"class":1935}," './counter.module.scss'\n",[138,8786,8788],{"id":8787},"creating-our-types","Creating our Types",[11,8790,8791],{},"The next thing we need to define is our types for our props. We want the consumer to be able to start the count at any number as well as end it at any number. It might be useful to have a count starting at zero for scoring or 1 for purchasing a product. We also want the consumer to be able to increment and decrement by a given amount such as 1 or 5. And finally we want to have a function that is called when the count is changed.",[299,8793,8795],{"className":4894,"code":8794,"language":4896,"meta":307,"style":307},"export type CounterProps = {\n  /**\n   * min Value of counter\n   */\n  min?: number,\n  /**\n   * max Value\n   */\n  max?: number,\n  /**\n   * increment value\n   */\n  increment?: number,\n  /**\n   * decrement value\n   */\n  decrement?: number,\n  /**\n   * a function that registers the count when changed\n   */\n  onCountChange: (count: number) => void\n} & React.HTMLAttributes\u003CHTMLDivElement>\n",[179,8796,8797,8810,8814,8819,8823,8836,8840,8845,8849,8860,8864,8869,8873,8884,8888,8893,8897,8908,8912,8917,8921,8945],{"__ignoreMap":307},[1736,8798,8799,8801,8803,8806,8808],{"class":1738,"line":1739},[1736,8800,6632],{"class":4866},[1736,8802,6635],{"class":4866},[1736,8804,8805],{"class":2674}," CounterProps",[1736,8807,4911],{"class":4866},[1736,8809,4914],{"class":1912},[1736,8811,8812],{"class":1738,"line":748},[1736,8813,6821],{"class":6820},[1736,8815,8816],{"class":1738,"line":756},[1736,8817,8818],{"class":6820},"   * min Value of counter\n",[1736,8820,8821],{"class":1738,"line":1755},[1736,8822,6831],{"class":6820},[1736,8824,8825,8828,8831,8834],{"class":1738,"line":1761},[1736,8826,8827],{"class":5036},"  min",[1736,8829,8830],{"class":4866},"?:",[1736,8832,8833],{"class":1918}," number",[1736,8835,1939],{"class":1912},[1736,8837,8838],{"class":1738,"line":1767},[1736,8839,6821],{"class":6820},[1736,8841,8842],{"class":1738,"line":1772},[1736,8843,8844],{"class":6820},"   * max Value\n",[1736,8846,8847],{"class":1738,"line":1778},[1736,8848,6831],{"class":6820},[1736,8850,8851,8854,8856,8858],{"class":1738,"line":1784},[1736,8852,8853],{"class":5036},"  max",[1736,8855,8830],{"class":4866},[1736,8857,8833],{"class":1918},[1736,8859,1939],{"class":1912},[1736,8861,8862],{"class":1738,"line":1790},[1736,8863,6821],{"class":6820},[1736,8865,8866],{"class":1738,"line":1796},[1736,8867,8868],{"class":6820},"   * increment value\n",[1736,8870,8871],{"class":1738,"line":2353},[1736,8872,6831],{"class":6820},[1736,8874,8875,8878,8880,8882],{"class":1738,"line":2358},[1736,8876,8877],{"class":5036},"  increment",[1736,8879,8830],{"class":4866},[1736,8881,8833],{"class":1918},[1736,8883,1939],{"class":1912},[1736,8885,8886],{"class":1738,"line":2364},[1736,8887,6821],{"class":6820},[1736,8889,8890],{"class":1738,"line":2370},[1736,8891,8892],{"class":6820},"   * decrement value\n",[1736,8894,8895],{"class":1738,"line":2376},[1736,8896,6831],{"class":6820},[1736,8898,8899,8902,8904,8906],{"class":1738,"line":2381},[1736,8900,8901],{"class":5036},"  decrement",[1736,8903,8830],{"class":4866},[1736,8905,8833],{"class":1918},[1736,8907,1939],{"class":1912},[1736,8909,8910],{"class":1738,"line":2387},[1736,8911,6821],{"class":6820},[1736,8913,8914],{"class":1738,"line":2393},[1736,8915,8916],{"class":6820},"   * a function that registers the count when changed\n",[1736,8918,8919],{"class":1738,"line":2398},[1736,8920,6831],{"class":6820},[1736,8922,8923,8926,8928,8930,8933,8935,8937,8940,8942],{"class":1738,"line":2404},[1736,8924,8925],{"class":2674},"  onCountChange",[1736,8927,1087],{"class":4866},[1736,8929,1095],{"class":1912},[1736,8931,8932],{"class":5036},"count",[1736,8934,1087],{"class":4866},[1736,8936,8833],{"class":1918},[1736,8938,8939],{"class":1912},") ",[1736,8941,7013],{"class":4866},[1736,8943,8944],{"class":1918}," void\n",[1736,8946,8947,8949,8951,8953,8955,8957,8959,8961],{"class":1738,"line":6959},[1736,8948,6871],{"class":1912},[1736,8950,6646],{"class":4866},[1736,8952,6649],{"class":2674},[1736,8954,891],{"class":1912},[1736,8956,6654],{"class":2674},[1736,8958,6657],{"class":1912},[1736,8960,6660],{"class":2674},[1736,8962,6663],{"class":1912},[138,8964,8966],{"id":8965},"creating-our-component","Creating our Component",[11,8968,8969,8970,829,8973,829,8976,608,8979,8982,8983,8986,8987,8990,8991,8994],{},"Next we need to create our component which we call Counter and pass in the props with some default values for the ",[179,8971,8972],{},"min",[179,8974,8975],{},"max",[179,8977,8978],{},"increment",[179,8980,8981],{},"decrement",". We then add in the components we need. The Button component has a prop of ",[179,8984,8985],{},"counter"," which will give us the styling we need for a counter button. It also accepts a prop of ",[179,8988,8989],{},"onClick"," which will be called when the button is clicked. We can give this the value of ",[179,8992,8993],{},"handleClickSubtract"," and later we can write the function for it to decrease the count.",[11,8996,8997,8998,608,9000,9002,9003,9005,9006,9009,9010,9013],{},"Next is our input component which will be of type Number and here we pass in the the ",[179,8999,8972],{},[179,9001,8975],{}," props as well as the value equal to ",[179,9004,8932],{}," and an ",[179,9007,9008],{},"onChange"," function that will be called when the value is changed. We will need to create the function for ",[179,9011,9012],{},"handleClick"," later.",[11,9015,9016,9017,9019,9020,9023],{},"And finally we add our next button component which will be responsible for decreasing the count. This accepts the prop of counter for styling and an ",[179,9018,8989],{}," with the value of ",[179,9021,9022],{},"handleClickAdd"," which we will create later.",[299,9025,9027],{"className":4894,"code":9026,"language":4896,"meta":307,"style":307},"...\nexport function Counter({\n  ...\n}: CounterProps) {\n\n  return (\n    \u003Cdiv className={styles.counter}>\n      \u003CButton counter onClick={handleClickSubtract}>\n        -\n      \u003C/Button>\n      \u003CInput\n        className={styles.counterInput}\n        type=\"number\"\n        min={min}\n        max={max}\n        value={count}\n        width=\"80px\"\n        onChange={handleClick}\n      />\n      \u003CButton counter onClick={handleClickAdd}>\n        +\n      \u003C/Button>\n    \u003C/div>\n  )\n}\n",[179,9028,9029,9033,9044,9049,9060,9064,9070,9083,9101,9106,9114,9121,9131,9141,9151,9161,9171,9181,9191,9196,9211,9216,9224,9232,9236],{"__ignoreMap":307},[1736,9030,9031],{"class":1738,"line":1739},[1736,9032,5073],{"class":4866},[1736,9034,9035,9037,9039,9042],{"class":1738,"line":748},[1736,9036,6632],{"class":4866},[1736,9038,6674],{"class":4866},[1736,9040,9041],{"class":2674}," Counter",[1736,9043,5122],{"class":1912},[1736,9045,9046],{"class":1738,"line":756},[1736,9047,9048],{"class":1912},"  ...\n",[1736,9050,9051,9054,9056,9058],{"class":1738,"line":1755},[1736,9052,9053],{"class":1912},"}",[1736,9055,1087],{"class":4866},[1736,9057,8805],{"class":2674},[1736,9059,7246],{"class":1912},[1736,9061,9062],{"class":1738,"line":1761},[1736,9063,1747],{"emptyLinePlaceholder":790},[1736,9065,9066,9068],{"class":1738,"line":1767},[1736,9067,6685],{"class":4866},[1736,9069,6688],{"class":1912},[1736,9071,9072,9074,9076,9078,9080],{"class":1738,"line":1772},[1736,9073,6693],{"class":1912},[1736,9075,6697],{"class":6696},[1736,9077,6700],{"class":2674},[1736,9079,5062],{"class":4866},[1736,9081,9082],{"class":1912},"{styles.counter}>\n",[1736,9084,9085,9087,9090,9093,9096,9098],{"class":1738,"line":1778},[1736,9086,6710],{"class":1912},[1736,9088,9089],{"class":1918},"Button",[1736,9091,9092],{"class":2674}," counter",[1736,9094,9095],{"class":2674}," onClick",[1736,9097,5062],{"class":4866},[1736,9099,9100],{"class":1912},"{handleClickSubtract}>\n",[1736,9102,9103],{"class":1738,"line":1784},[1736,9104,9105],{"class":1912},"        -\n",[1736,9107,9108,9110,9112],{"class":1738,"line":1790},[1736,9109,8261],{"class":1912},[1736,9111,9089],{"class":1918},[1736,9113,6663],{"class":1912},[1736,9115,9116,9118],{"class":1738,"line":1796},[1736,9117,6710],{"class":1912},[1736,9119,9120],{"class":1918},"Input\n",[1736,9122,9123,9126,9128],{"class":1738,"line":2353},[1736,9124,9125],{"class":2674},"        className",[1736,9127,5062],{"class":4866},[1736,9129,9130],{"class":1912},"{styles.counterInput}\n",[1736,9132,9133,9136,9138],{"class":1738,"line":2358},[1736,9134,9135],{"class":2674},"        type",[1736,9137,5062],{"class":4866},[1736,9139,9140],{"class":1935},"\"number\"\n",[1736,9142,9143,9146,9148],{"class":1738,"line":2364},[1736,9144,9145],{"class":2674},"        min",[1736,9147,5062],{"class":4866},[1736,9149,9150],{"class":1912},"{min}\n",[1736,9152,9153,9156,9158],{"class":1738,"line":2370},[1736,9154,9155],{"class":2674},"        max",[1736,9157,5062],{"class":4866},[1736,9159,9160],{"class":1912},"{max}\n",[1736,9162,9163,9166,9168],{"class":1738,"line":2376},[1736,9164,9165],{"class":2674},"        value",[1736,9167,5062],{"class":4866},[1736,9169,9170],{"class":1912},"{count}\n",[1736,9172,9173,9176,9178],{"class":1738,"line":2381},[1736,9174,9175],{"class":2674},"        width",[1736,9177,5062],{"class":4866},[1736,9179,9180],{"class":1935},"\"80px\"\n",[1736,9182,9183,9186,9188],{"class":1738,"line":2387},[1736,9184,9185],{"class":2674},"        onChange",[1736,9187,5062],{"class":4866},[1736,9189,9190],{"class":1912},"{handleClick}\n",[1736,9192,9193],{"class":1738,"line":2393},[1736,9194,9195],{"class":1912},"      />\n",[1736,9197,9198,9200,9202,9204,9206,9208],{"class":1738,"line":2398},[1736,9199,6710],{"class":1912},[1736,9201,9089],{"class":1918},[1736,9203,9092],{"class":2674},[1736,9205,9095],{"class":2674},[1736,9207,5062],{"class":4866},[1736,9209,9210],{"class":1912},"{handleClickAdd}>\n",[1736,9212,9213],{"class":1738,"line":2404},[1736,9214,9215],{"class":1912},"        +\n",[1736,9217,9218,9220,9222],{"class":1738,"line":6959},[1736,9219,8261],{"class":1912},[1736,9221,9089],{"class":1918},[1736,9223,6663],{"class":1912},[1736,9225,9226,9228,9230],{"class":1738,"line":7296},[1736,9227,6744],{"class":1912},[1736,9229,6697],{"class":6696},[1736,9231,6663],{"class":1912},[1736,9233,9234],{"class":1738,"line":7305},[1736,9235,6753],{"class":1912},[1736,9237,9238],{"class":1738,"line":7310},[1736,9239,1976],{"class":1912},[11,9241,9242,9243,9246,9247,9249,9250,9252],{},"Now that we have our component setup visually we need to add functionality to it. We will use the ",[179,9244,9245],{},"useState"," hook to create a state variable called ",[179,9248,8932],{}," and set the default to be value of our ",[179,9251,8972],{}," prop.",[138,9254,9256],{"id":9255},"using-state","Using State",[299,9258,9260],{"className":8734,"code":9259,"language":8736,"meta":307,"style":307},"const [count, setCount] = useState(min)\n",[179,9261,9262],{"__ignoreMap":307},[1736,9263,9264,9266,9268,9270,9272,9275,9278,9280,9283],{"class":1738,"line":1739},[1736,9265,5029],{"class":4866},[1736,9267,8409],{"class":1912},[1736,9269,8932],{"class":1918},[1736,9271,829],{"class":1912},[1736,9273,9274],{"class":1918},"setCount",[1736,9276,9277],{"class":1912},"] ",[1736,9279,5062],{"class":4866},[1736,9281,9282],{"class":2674}," useState",[1736,9284,9285],{"class":1912},"(min)\n",[11,9287,9288,9289,9291,9292,9294,9295,9297,9298,9301],{},"We will also create a function called ",[179,9290,9022],{}," function that will check to see if the count value is less the the ",[179,9293,8975],{}," value and if it is then we will increase the count by the ",[179,9296,8978],{}," value. It will also call the ",[179,9299,9300],{},"onCountChange"," function and increment the count. This function is what will let us pass the count value to another component if needed.",[138,9303,9305],{"id":9304},"add-function","Add Function",[299,9307,9309],{"className":8734,"code":9308,"language":8736,"meta":307,"style":307},"function handleClickAdd() {\n  if (count \u003C max) {\n    setCount(count + increment)\n    onCountChange(count + increment)\n  }\n}\n",[179,9310,9311,9320,9333,9347,9358,9362],{"__ignoreMap":307},[1736,9312,9313,9315,9318],{"class":1738,"line":1739},[1736,9314,7745],{"class":4866},[1736,9316,9317],{"class":2674}," handleClickAdd",[1736,9319,6680],{"class":1912},[1736,9321,9322,9325,9328,9330],{"class":1738,"line":748},[1736,9323,9324],{"class":4866},"  if",[1736,9326,9327],{"class":1912}," (count ",[1736,9329,6657],{"class":4866},[1736,9331,9332],{"class":1912}," max) {\n",[1736,9334,9335,9338,9341,9344],{"class":1738,"line":756},[1736,9336,9337],{"class":2674},"    setCount",[1736,9339,9340],{"class":1912},"(count ",[1736,9342,9343],{"class":4866},"+",[1736,9345,9346],{"class":1912}," increment)\n",[1736,9348,9349,9352,9354,9356],{"class":1738,"line":1755},[1736,9350,9351],{"class":2674},"    onCountChange",[1736,9353,9340],{"class":1912},[1736,9355,9343],{"class":4866},[1736,9357,9346],{"class":1912},[1736,9359,9360],{"class":1738,"line":1761},[1736,9361,1971],{"class":1912},[1736,9363,9364],{"class":1738,"line":1767},[1736,9365,1976],{"class":1912},[138,9367,9369],{"id":9368},"subtract-function","Subtract Function",[11,9371,9372,9373,9375,9376,9378,9379,9381,9382,9384],{},"We then need to do the same for the ",[179,9374,8993],{}," function and if the count is greater than the ",[179,9377,8972],{}," value then we will decrease the count by the ",[179,9380,8981],{}," value as well as call the ",[179,9383,9300],{}," function.",[299,9386,9388],{"className":8734,"code":9387,"language":8736,"meta":307,"style":307},"function handleClickSubtract() {\n  if (count > min) {\n    setCount(count - decrement)\n    onCountChange(count - decrement)\n  }\n}\n",[179,9389,9390,9399,9411,9423,9433,9437],{"__ignoreMap":307},[1736,9391,9392,9394,9397],{"class":1738,"line":1739},[1736,9393,7745],{"class":4866},[1736,9395,9396],{"class":2674}," handleClickSubtract",[1736,9398,6680],{"class":1912},[1736,9400,9401,9403,9405,9408],{"class":1738,"line":748},[1736,9402,9324],{"class":4866},[1736,9404,9327],{"class":1912},[1736,9406,9407],{"class":4866},">",[1736,9409,9410],{"class":1912}," min) {\n",[1736,9412,9413,9415,9417,9420],{"class":1738,"line":756},[1736,9414,9337],{"class":2674},[1736,9416,9340],{"class":1912},[1736,9418,9419],{"class":4866},"-",[1736,9421,9422],{"class":1912}," decrement)\n",[1736,9424,9425,9427,9429,9431],{"class":1738,"line":1755},[1736,9426,9351],{"class":2674},[1736,9428,9340],{"class":1912},[1736,9430,9419],{"class":4866},[1736,9432,9422],{"class":1912},[1736,9434,9435],{"class":1738,"line":1761},[1736,9436,1971],{"class":1912},[1736,9438,9439],{"class":1738,"line":1767},[1736,9440,1976],{"class":1912},[138,9442,9444],{"id":9443},"handle-click-function","Handle Click Function",[11,9446,9447,9448,9450,9451,9453,9454,9456,9457,9384],{},"Next is our ",[179,9449,9012],{}," function which will be called when the input value is manually changed. We will check to see if the value is less than the ",[179,9452,8975],{}," value and if it is then we will set the ",[179,9455,8932],{}," to the value of the input as well as update the ",[179,9458,9300],{},[299,9460,9462],{"className":8734,"code":9461,"language":8736,"meta":307,"style":307},"function handleClick(e) {\n  setCount(e.target.valueAsNumber)\n  onCountChange(e.target.valueAsNumber)\n}\n",[179,9463,9464,9478,9486,9492],{"__ignoreMap":307},[1736,9465,9466,9468,9471,9473,9476],{"class":1738,"line":1739},[1736,9467,7745],{"class":4866},[1736,9469,9470],{"class":2674}," handleClick",[1736,9472,7751],{"class":1912},[1736,9474,9475],{"class":5036},"e",[1736,9477,7246],{"class":1912},[1736,9479,9480,9483],{"class":1738,"line":748},[1736,9481,9482],{"class":2674},"  setCount",[1736,9484,9485],{"class":1912},"(e.target.valueAsNumber)\n",[1736,9487,9488,9490],{"class":1738,"line":756},[1736,9489,8925],{"class":2674},[1736,9491,9485],{"class":1912},[1736,9493,9494],{"class":1738,"line":1755},[1736,9495,1976],{"class":1912},[138,9497,9499],{"id":9498},"full-code","Full Code",[11,9501,9502],{},"And now our counter component is ready to go.",[299,9504,9506],{"className":8734,"code":9505,"language":8736,"meta":307,"style":307},"import React, { useState } from 'react'\nimport { Button } from '@learn-bit-react/base-ui.ui.button'\nimport { Input } from '@learn-bit-react/base-ui.ui.forms.input'\nimport styles from './counter.module.scss'\n\nexport type CounterProps = {\n  /**\n   * min Value of counter\n   */\n  min?: number\n  /**\n   * max Value\n   */\n  max?: number\n  /**\n   * increment value\n   */\n  increment?: number\n  /**\n   * decrement value\n   */\n  decrement?: number\n  /**\n   * a function that registers the count when changed\n   */\n  onCountChange: (count: number) => void\n} & React.HTMLAttributes\u003CHTMLDivElement>\n\nexport function Counter({\n  min = 1,\n  max = 20,\n  increment = 1,\n  decrement = 1,\n  onCountChange\n}: CounterProps) {\n  const [count, setCount] = useState(min)\n\n  function handleClickAdd() {\n    if (count \u003C max) {\n      setCount(count + increment)\n      onCountChange(count + increment)\n    }\n  }\n  function handleClickSubtract() {\n    if (count > min) {\n      setCount(count - decrement)\n      onCountChange(count - decrement)\n    }\n  }\n\n  function handleClick(e) {\n    setCount(e.target.valueAsNumber)\n    onCountChange(e.target.valueAsNumber)\n  }\n\n  return (\n    \u003Cdiv className={styles.counter}>\n      \u003CButton counter onClick={handleClickSubtract}>\n        -\n      \u003C/Button>\n      \u003CInput\n        className={styles.counterInput}\n        type=\"number\"\n        min={min}\n        max={max}\n        value={count}\n        width=\"80px\"\n        onChange={handleClick}\n      />\n      \u003CButton counter onClick={handleClickAdd}>\n        +\n      \u003C/Button>\n    \u003C/div>\n  )\n}\n",[179,9507,9508,9518,9528,9538,9548,9552,9564,9568,9572,9576,9585,9589,9593,9597,9605,9609,9613,9617,9625,9629,9633,9637,9645,9649,9653,9657,9678,9697,9702,9713,9725,9737,9748,9759,9765,9776,9797,9802,9812,9824,9836,9848,9854,9859,9868,9879,9890,9901,9906,9911,9916,9929,9936,9943,9948,9953,9960,9973,9988,9993,10002,10009,10018,10027,10036,10045,10054,10063,10072,10077,10092,10097,10106,10115,10120],{"__ignoreMap":307},[1736,9509,9510,9512,9514,9516],{"class":1738,"line":1739},[1736,9511,4996],{"class":4866},[1736,9513,8745],{"class":1912},[1736,9515,5002],{"class":4866},[1736,9517,6599],{"class":1935},[1736,9519,9520,9522,9524,9526],{"class":1738,"line":748},[1736,9521,4996],{"class":4866},[1736,9523,8756],{"class":1912},[1736,9525,5002],{"class":4866},[1736,9527,8761],{"class":1935},[1736,9529,9530,9532,9534,9536],{"class":1738,"line":756},[1736,9531,4996],{"class":4866},[1736,9533,8768],{"class":1912},[1736,9535,5002],{"class":4866},[1736,9537,8773],{"class":1935},[1736,9539,9540,9542,9544,9546],{"class":1738,"line":1755},[1736,9541,4996],{"class":4866},[1736,9543,6618],{"class":1912},[1736,9545,5002],{"class":4866},[1736,9547,8784],{"class":1935},[1736,9549,9550],{"class":1738,"line":1761},[1736,9551,1747],{"emptyLinePlaceholder":790},[1736,9553,9554,9556,9558,9560,9562],{"class":1738,"line":1767},[1736,9555,6632],{"class":4866},[1736,9557,6635],{"class":4866},[1736,9559,8805],{"class":2674},[1736,9561,4911],{"class":4866},[1736,9563,4914],{"class":1912},[1736,9565,9566],{"class":1738,"line":1772},[1736,9567,6821],{"class":6820},[1736,9569,9570],{"class":1738,"line":1778},[1736,9571,8818],{"class":6820},[1736,9573,9574],{"class":1738,"line":1784},[1736,9575,6831],{"class":6820},[1736,9577,9578,9580,9582],{"class":1738,"line":1790},[1736,9579,8827],{"class":5036},[1736,9581,8830],{"class":4866},[1736,9583,9584],{"class":1918}," number\n",[1736,9586,9587],{"class":1738,"line":1796},[1736,9588,6821],{"class":6820},[1736,9590,9591],{"class":1738,"line":2353},[1736,9592,8844],{"class":6820},[1736,9594,9595],{"class":1738,"line":2358},[1736,9596,6831],{"class":6820},[1736,9598,9599,9601,9603],{"class":1738,"line":2364},[1736,9600,8853],{"class":5036},[1736,9602,8830],{"class":4866},[1736,9604,9584],{"class":1918},[1736,9606,9607],{"class":1738,"line":2370},[1736,9608,6821],{"class":6820},[1736,9610,9611],{"class":1738,"line":2376},[1736,9612,8868],{"class":6820},[1736,9614,9615],{"class":1738,"line":2381},[1736,9616,6831],{"class":6820},[1736,9618,9619,9621,9623],{"class":1738,"line":2387},[1736,9620,8877],{"class":5036},[1736,9622,8830],{"class":4866},[1736,9624,9584],{"class":1918},[1736,9626,9627],{"class":1738,"line":2393},[1736,9628,6821],{"class":6820},[1736,9630,9631],{"class":1738,"line":2398},[1736,9632,8892],{"class":6820},[1736,9634,9635],{"class":1738,"line":2404},[1736,9636,6831],{"class":6820},[1736,9638,9639,9641,9643],{"class":1738,"line":6959},[1736,9640,8901],{"class":5036},[1736,9642,8830],{"class":4866},[1736,9644,9584],{"class":1918},[1736,9646,9647],{"class":1738,"line":7296},[1736,9648,6821],{"class":6820},[1736,9650,9651],{"class":1738,"line":7305},[1736,9652,8916],{"class":6820},[1736,9654,9655],{"class":1738,"line":7310},[1736,9656,6831],{"class":6820},[1736,9658,9660,9662,9664,9666,9668,9670,9672,9674,9676],{"class":1738,"line":9659},26,[1736,9661,8925],{"class":2674},[1736,9663,1087],{"class":4866},[1736,9665,1095],{"class":1912},[1736,9667,8932],{"class":5036},[1736,9669,1087],{"class":4866},[1736,9671,8833],{"class":1918},[1736,9673,8939],{"class":1912},[1736,9675,7013],{"class":4866},[1736,9677,8944],{"class":1918},[1736,9679,9681,9683,9685,9687,9689,9691,9693,9695],{"class":1738,"line":9680},27,[1736,9682,6871],{"class":1912},[1736,9684,6646],{"class":4866},[1736,9686,6649],{"class":2674},[1736,9688,891],{"class":1912},[1736,9690,6654],{"class":2674},[1736,9692,6657],{"class":1912},[1736,9694,6660],{"class":2674},[1736,9696,6663],{"class":1912},[1736,9698,9700],{"class":1738,"line":9699},28,[1736,9701,1747],{"emptyLinePlaceholder":790},[1736,9703,9705,9707,9709,9711],{"class":1738,"line":9704},29,[1736,9706,6632],{"class":4866},[1736,9708,6674],{"class":4866},[1736,9710,9041],{"class":2674},[1736,9712,5122],{"class":1912},[1736,9714,9716,9718,9720,9723],{"class":1738,"line":9715},30,[1736,9717,8827],{"class":5036},[1736,9719,4911],{"class":4866},[1736,9721,9722],{"class":1918}," 1",[1736,9724,1939],{"class":1912},[1736,9726,9728,9730,9732,9735],{"class":1738,"line":9727},31,[1736,9729,8853],{"class":5036},[1736,9731,4911],{"class":4866},[1736,9733,9734],{"class":1918}," 20",[1736,9736,1939],{"class":1912},[1736,9738,9740,9742,9744,9746],{"class":1738,"line":9739},32,[1736,9741,8877],{"class":5036},[1736,9743,4911],{"class":4866},[1736,9745,9722],{"class":1918},[1736,9747,1939],{"class":1912},[1736,9749,9751,9753,9755,9757],{"class":1738,"line":9750},33,[1736,9752,8901],{"class":5036},[1736,9754,4911],{"class":4866},[1736,9756,9722],{"class":1918},[1736,9758,1939],{"class":1912},[1736,9760,9762],{"class":1738,"line":9761},34,[1736,9763,9764],{"class":5036},"  onCountChange\n",[1736,9766,9768,9770,9772,9774],{"class":1738,"line":9767},35,[1736,9769,9053],{"class":1912},[1736,9771,1087],{"class":4866},[1736,9773,8805],{"class":2674},[1736,9775,7246],{"class":1912},[1736,9777,9779,9781,9783,9785,9787,9789,9791,9793,9795],{"class":1738,"line":9778},36,[1736,9780,7824],{"class":4866},[1736,9782,8409],{"class":1912},[1736,9784,8932],{"class":1918},[1736,9786,829],{"class":1912},[1736,9788,9274],{"class":1918},[1736,9790,9277],{"class":1912},[1736,9792,5062],{"class":4866},[1736,9794,9282],{"class":2674},[1736,9796,9285],{"class":1912},[1736,9798,9800],{"class":1738,"line":9799},37,[1736,9801,1747],{"emptyLinePlaceholder":790},[1736,9803,9805,9808,9810],{"class":1738,"line":9804},38,[1736,9806,9807],{"class":4866},"  function",[1736,9809,9317],{"class":2674},[1736,9811,6680],{"class":1912},[1736,9813,9815,9818,9820,9822],{"class":1738,"line":9814},39,[1736,9816,9817],{"class":4866},"    if",[1736,9819,9327],{"class":1912},[1736,9821,6657],{"class":4866},[1736,9823,9332],{"class":1912},[1736,9825,9827,9830,9832,9834],{"class":1738,"line":9826},40,[1736,9828,9829],{"class":2674},"      setCount",[1736,9831,9340],{"class":1912},[1736,9833,9343],{"class":4866},[1736,9835,9346],{"class":1912},[1736,9837,9839,9842,9844,9846],{"class":1738,"line":9838},41,[1736,9840,9841],{"class":2674},"      onCountChange",[1736,9843,9340],{"class":1912},[1736,9845,9343],{"class":4866},[1736,9847,9346],{"class":1912},[1736,9849,9851],{"class":1738,"line":9850},42,[1736,9852,9853],{"class":1912},"    }\n",[1736,9855,9857],{"class":1738,"line":9856},43,[1736,9858,1971],{"class":1912},[1736,9860,9862,9864,9866],{"class":1738,"line":9861},44,[1736,9863,9807],{"class":4866},[1736,9865,9396],{"class":2674},[1736,9867,6680],{"class":1912},[1736,9869,9871,9873,9875,9877],{"class":1738,"line":9870},45,[1736,9872,9817],{"class":4866},[1736,9874,9327],{"class":1912},[1736,9876,9407],{"class":4866},[1736,9878,9410],{"class":1912},[1736,9880,9882,9884,9886,9888],{"class":1738,"line":9881},46,[1736,9883,9829],{"class":2674},[1736,9885,9340],{"class":1912},[1736,9887,9419],{"class":4866},[1736,9889,9422],{"class":1912},[1736,9891,9893,9895,9897,9899],{"class":1738,"line":9892},47,[1736,9894,9841],{"class":2674},[1736,9896,9340],{"class":1912},[1736,9898,9419],{"class":4866},[1736,9900,9422],{"class":1912},[1736,9902,9904],{"class":1738,"line":9903},48,[1736,9905,9853],{"class":1912},[1736,9907,9909],{"class":1738,"line":9908},49,[1736,9910,1971],{"class":1912},[1736,9912,9914],{"class":1738,"line":9913},50,[1736,9915,1747],{"emptyLinePlaceholder":790},[1736,9917,9919,9921,9923,9925,9927],{"class":1738,"line":9918},51,[1736,9920,9807],{"class":4866},[1736,9922,9470],{"class":2674},[1736,9924,7751],{"class":1912},[1736,9926,9475],{"class":5036},[1736,9928,7246],{"class":1912},[1736,9930,9932,9934],{"class":1738,"line":9931},52,[1736,9933,9337],{"class":2674},[1736,9935,9485],{"class":1912},[1736,9937,9939,9941],{"class":1738,"line":9938},53,[1736,9940,9351],{"class":2674},[1736,9942,9485],{"class":1912},[1736,9944,9946],{"class":1738,"line":9945},54,[1736,9947,1971],{"class":1912},[1736,9949,9951],{"class":1738,"line":9950},55,[1736,9952,1747],{"emptyLinePlaceholder":790},[1736,9954,9956,9958],{"class":1738,"line":9955},56,[1736,9957,6685],{"class":4866},[1736,9959,6688],{"class":1912},[1736,9961,9963,9965,9967,9969,9971],{"class":1738,"line":9962},57,[1736,9964,6693],{"class":1912},[1736,9966,6697],{"class":6696},[1736,9968,6700],{"class":2674},[1736,9970,5062],{"class":4866},[1736,9972,9082],{"class":1912},[1736,9974,9976,9978,9980,9982,9984,9986],{"class":1738,"line":9975},58,[1736,9977,6710],{"class":1912},[1736,9979,9089],{"class":1918},[1736,9981,9092],{"class":2674},[1736,9983,9095],{"class":2674},[1736,9985,5062],{"class":4866},[1736,9987,9100],{"class":1912},[1736,9989,9991],{"class":1738,"line":9990},59,[1736,9992,9105],{"class":1912},[1736,9994,9996,9998,10000],{"class":1738,"line":9995},60,[1736,9997,8261],{"class":1912},[1736,9999,9089],{"class":1918},[1736,10001,6663],{"class":1912},[1736,10003,10005,10007],{"class":1738,"line":10004},61,[1736,10006,6710],{"class":1912},[1736,10008,9120],{"class":1918},[1736,10010,10012,10014,10016],{"class":1738,"line":10011},62,[1736,10013,9125],{"class":2674},[1736,10015,5062],{"class":4866},[1736,10017,9130],{"class":1912},[1736,10019,10021,10023,10025],{"class":1738,"line":10020},63,[1736,10022,9135],{"class":2674},[1736,10024,5062],{"class":4866},[1736,10026,9140],{"class":1935},[1736,10028,10030,10032,10034],{"class":1738,"line":10029},64,[1736,10031,9145],{"class":2674},[1736,10033,5062],{"class":4866},[1736,10035,9150],{"class":1912},[1736,10037,10039,10041,10043],{"class":1738,"line":10038},65,[1736,10040,9155],{"class":2674},[1736,10042,5062],{"class":4866},[1736,10044,9160],{"class":1912},[1736,10046,10048,10050,10052],{"class":1738,"line":10047},66,[1736,10049,9165],{"class":2674},[1736,10051,5062],{"class":4866},[1736,10053,9170],{"class":1912},[1736,10055,10057,10059,10061],{"class":1738,"line":10056},67,[1736,10058,9175],{"class":2674},[1736,10060,5062],{"class":4866},[1736,10062,9180],{"class":1935},[1736,10064,10066,10068,10070],{"class":1738,"line":10065},68,[1736,10067,9185],{"class":2674},[1736,10069,5062],{"class":4866},[1736,10071,9190],{"class":1912},[1736,10073,10075],{"class":1738,"line":10074},69,[1736,10076,9195],{"class":1912},[1736,10078,10080,10082,10084,10086,10088,10090],{"class":1738,"line":10079},70,[1736,10081,6710],{"class":1912},[1736,10083,9089],{"class":1918},[1736,10085,9092],{"class":2674},[1736,10087,9095],{"class":2674},[1736,10089,5062],{"class":4866},[1736,10091,9210],{"class":1912},[1736,10093,10095],{"class":1738,"line":10094},71,[1736,10096,9215],{"class":1912},[1736,10098,10100,10102,10104],{"class":1738,"line":10099},72,[1736,10101,8261],{"class":1912},[1736,10103,9089],{"class":1918},[1736,10105,6663],{"class":1912},[1736,10107,10109,10111,10113],{"class":1738,"line":10108},73,[1736,10110,6744],{"class":1912},[1736,10112,6697],{"class":6696},[1736,10114,6663],{"class":1912},[1736,10116,10118],{"class":1738,"line":10117},74,[1736,10119,6753],{"class":1912},[1736,10121,10123],{"class":1738,"line":10122},75,[1736,10124,1976],{"class":1912},[23,10126,10128],{"id":10127},"creating-our-compositions","Creating our Compositions",[11,10130,10131],{},"When building our components we need to see what is going on and for this in Bit we use compositions so we can build our component as an independent component and see it in isolation using bits dev server. However if you are not using bit you can just import the component into another component or into your app and see it working there.",[11,10133,10134,10135,10138,10139,10141,10142,10144],{},"Our composition uses our previously created Counter component and also shows what the count value is in a ",[179,10136,10137],{},"\u003Cp>"," tag which of course could be another component. This shows us that our count value is being updated in the UI in real time. We are not passing in any ",[179,10140,8972],{}," or ",[179,10143,8975],{}," values here as we want to use the default values but we could create another composition that shows a different counter with different values.",[299,10146,10148],{"className":8734,"code":10147,"language":8736,"meta":307,"style":307},"import React, { useState } from 'react'\nimport { Counter } from './counter'\n\nexport const BasicCounter = () => \u003CCounter onCountChange={count => count} />\n\nexport function CounterWithSetCount() {\n  const [count, setCount] = useState(1)\n  return (\n    \u003C>\n      \u003CCounter\n        onCountChange={(count) => {\n          setCount(count)\n        }}\n      />\n      \u003Cp>Count is: {count}\u003C/p>\n    \u003C/>\n  )\n}\n",[179,10149,10150,10160,10172,10176,10212,10216,10227,10252,10258,10263,10270,10288,10296,10301,10305,10318,10323,10327],{"__ignoreMap":307},[1736,10151,10152,10154,10156,10158],{"class":1738,"line":1739},[1736,10153,4996],{"class":4866},[1736,10155,8745],{"class":1912},[1736,10157,5002],{"class":4866},[1736,10159,6599],{"class":1935},[1736,10161,10162,10164,10167,10169],{"class":1738,"line":748},[1736,10163,4996],{"class":4866},[1736,10165,10166],{"class":1912}," { Counter } ",[1736,10168,5002],{"class":4866},[1736,10170,10171],{"class":1935}," './counter'\n",[1736,10173,10174],{"class":1738,"line":756},[1736,10175,1747],{"emptyLinePlaceholder":790},[1736,10177,10178,10180,10182,10185,10187,10189,10191,10194,10197,10200,10202,10204,10206,10209],{"class":1738,"line":1755},[1736,10179,6632],{"class":4866},[1736,10181,7002],{"class":4866},[1736,10183,10184],{"class":2674}," BasicCounter",[1736,10186,4911],{"class":4866},[1736,10188,7010],{"class":1912},[1736,10190,7013],{"class":4866},[1736,10192,10193],{"class":1912}," \u003C",[1736,10195,10196],{"class":1918},"Counter",[1736,10198,10199],{"class":2674}," onCountChange",[1736,10201,5062],{"class":4866},[1736,10203,7387],{"class":1912},[1736,10205,8932],{"class":5036},[1736,10207,10208],{"class":4866}," =>",[1736,10210,10211],{"class":1912}," count} />\n",[1736,10213,10214],{"class":1738,"line":1761},[1736,10215,1747],{"emptyLinePlaceholder":790},[1736,10217,10218,10220,10222,10225],{"class":1738,"line":1767},[1736,10219,6632],{"class":4866},[1736,10221,6674],{"class":4866},[1736,10223,10224],{"class":2674}," CounterWithSetCount",[1736,10226,6680],{"class":1912},[1736,10228,10229,10231,10233,10235,10237,10239,10241,10243,10245,10247,10250],{"class":1738,"line":1772},[1736,10230,7824],{"class":4866},[1736,10232,8409],{"class":1912},[1736,10234,8932],{"class":1918},[1736,10236,829],{"class":1912},[1736,10238,9274],{"class":1918},[1736,10240,9277],{"class":1912},[1736,10242,5062],{"class":4866},[1736,10244,9282],{"class":2674},[1736,10246,7751],{"class":1912},[1736,10248,10249],{"class":1918},"1",[1736,10251,7045],{"class":1912},[1736,10253,10254,10256],{"class":1738,"line":1778},[1736,10255,6685],{"class":4866},[1736,10257,6688],{"class":1912},[1736,10259,10260],{"class":1738,"line":1784},[1736,10261,10262],{"class":1912},"    \u003C>\n",[1736,10264,10265,10267],{"class":1738,"line":1790},[1736,10266,6710],{"class":1912},[1736,10268,10269],{"class":1918},"Counter\n",[1736,10271,10272,10275,10277,10280,10282,10284,10286],{"class":1738,"line":1796},[1736,10273,10274],{"class":2674},"        onCountChange",[1736,10276,5062],{"class":4866},[1736,10278,10279],{"class":1912},"{(",[1736,10281,8932],{"class":5036},[1736,10283,8939],{"class":1912},[1736,10285,7013],{"class":4866},[1736,10287,4914],{"class":1912},[1736,10289,10290,10293],{"class":1738,"line":2353},[1736,10291,10292],{"class":2674},"          setCount",[1736,10294,10295],{"class":1912},"(count)\n",[1736,10297,10298],{"class":1738,"line":2358},[1736,10299,10300],{"class":1912},"        }}\n",[1736,10302,10303],{"class":1738,"line":2364},[1736,10304,9195],{"class":1912},[1736,10306,10307,10309,10311,10314,10316],{"class":1738,"line":2370},[1736,10308,6710],{"class":1912},[1736,10310,11],{"class":6696},[1736,10312,10313],{"class":1912},">Count is: {count}\u003C/",[1736,10315,11],{"class":6696},[1736,10317,6663],{"class":1912},[1736,10319,10320],{"class":1738,"line":2376},[1736,10321,10322],{"class":1912},"    \u003C/>\n",[1736,10324,10325],{"class":1738,"line":2381},[1736,10326,6753],{"class":1912},[1736,10328,10329],{"class":1738,"line":2387},[1736,10330,1976],{"class":1912},[23,10332,10334],{"id":10333},"testing-our-counter","Testing Our Counter",[11,10336,10337],{},"The final step is to write some tests for our counter to make sure it works as it should. Of course some would say that you should create the tests first but that's up in which way you want to build things.",[11,10339,10340,10341,608,10344,10347,10348,10351,10352,10355,10356,10359,10360,891],{},"In our test file we need to import React, as well as the ",[179,10342,10343],{},"render",[179,10345,10346],{},"screen"," functions from ",[179,10349,10350],{},"@testing-library/react"," and our ",[179,10353,10354],{},"BasicCounter"," component. As we want to test the buttons we need to add the ",[179,10357,10358],{},"userEvent"," method from ",[179,10361,10362],{},"@testing-library/user-event",[299,10364,10366],{"className":8734,"code":10365,"language":8736,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { BasicCounter } from './counter.composition'\n",[179,10367,10368,10378,10390,10402],{"__ignoreMap":307},[1736,10369,10370,10372,10374,10376],{"class":1738,"line":1739},[1736,10371,4996],{"class":4866},[1736,10373,6594],{"class":1912},[1736,10375,5002],{"class":4866},[1736,10377,6599],{"class":1935},[1736,10379,10380,10382,10385,10387],{"class":1738,"line":748},[1736,10381,4996],{"class":4866},[1736,10383,10384],{"class":1912}," { render, screen } ",[1736,10386,5002],{"class":4866},[1736,10388,10389],{"class":1935}," '@testing-library/react'\n",[1736,10391,10392,10394,10397,10399],{"class":1738,"line":756},[1736,10393,4996],{"class":4866},[1736,10395,10396],{"class":1912}," userEvent ",[1736,10398,5002],{"class":4866},[1736,10400,10401],{"class":1935}," '@testing-library/user-event'\n",[1736,10403,10404,10406,10409,10411],{"class":1738,"line":1755},[1736,10405,4996],{"class":4866},[1736,10407,10408],{"class":1912}," { BasicCounter } ",[1736,10410,5002],{"class":4866},[1736,10412,10413],{"class":1935}," './counter.composition'\n",[11,10415,10416],{},"We can then think about what we want to test and write todos for each of them.",[299,10418,10420],{"className":8734,"code":10419,"language":8736,"meta":307,"style":307},"it.todo('should render a counter with value of 1')\n\nit.todo('should increase count when plus button is clicked')\n\nit.todo('should decrease count when minus button is clicked')\n\nit.todo('should not decrease to less than 1')\n",[179,10421,10422,10437,10441,10454,10458,10471,10475],{"__ignoreMap":307},[1736,10423,10424,10427,10430,10432,10435],{"class":1738,"line":1739},[1736,10425,10426],{"class":1912},"it.",[1736,10428,10429],{"class":2674},"todo",[1736,10431,7751],{"class":1912},[1736,10433,10434],{"class":1935},"'should render a counter with value of 1'",[1736,10436,7045],{"class":1912},[1736,10438,10439],{"class":1738,"line":748},[1736,10440,1747],{"emptyLinePlaceholder":790},[1736,10442,10443,10445,10447,10449,10452],{"class":1738,"line":756},[1736,10444,10426],{"class":1912},[1736,10446,10429],{"class":2674},[1736,10448,7751],{"class":1912},[1736,10450,10451],{"class":1935},"'should increase count when plus button is clicked'",[1736,10453,7045],{"class":1912},[1736,10455,10456],{"class":1738,"line":1755},[1736,10457,1747],{"emptyLinePlaceholder":790},[1736,10459,10460,10462,10464,10466,10469],{"class":1738,"line":1761},[1736,10461,10426],{"class":1912},[1736,10463,10429],{"class":2674},[1736,10465,7751],{"class":1912},[1736,10467,10468],{"class":1935},"'should decrease count when minus button is clicked'",[1736,10470,7045],{"class":1912},[1736,10472,10473],{"class":1738,"line":1767},[1736,10474,1747],{"emptyLinePlaceholder":790},[1736,10476,10477,10479,10481,10483,10486],{"class":1738,"line":1772},[1736,10478,10426],{"class":1912},[1736,10480,10429],{"class":2674},[1736,10482,7751],{"class":1912},[1736,10484,10485],{"class":1935},"'should not decrease to less than 1'",[1736,10487,7045],{"class":1912},[11,10489,10490,10491,10493,10494,10496,10497,10499,10500,10503,10504,10507],{},"We then render our ",[179,10492,10354],{}," component and use the ",[179,10495,10346],{}," method with the function of ",[179,10498,1032],{},". As we don't know what role is available to us we can use ",[179,10501,10502],{},"screen.getByRole('blah')"," which shows us that blah doesn't exist but that ",[179,10505,10506],{},"spinbutton"," does which is the role for our input of type number. We then expect our counter to have the value of 1 which was the min default value.",[299,10509,10511],{"className":8734,"code":10510,"language":8736,"meta":307,"style":307},"it('should render a counter with value of 1', () => {\n  render(\u003CBasicCounter />)\n  basicCounter = screen.getByRole('spinbutton')\n  expect(basicCounter).toHaveValue(1)\n})\n",[179,10512,10513,10529,10542,10561,10578],{"__ignoreMap":307},[1736,10514,10515,10518,10520,10522,10525,10527],{"class":1738,"line":1739},[1736,10516,10517],{"class":2674},"it",[1736,10519,7751],{"class":1912},[1736,10521,10434],{"class":1935},[1736,10523,10524],{"class":1912},", () ",[1736,10526,7013],{"class":4866},[1736,10528,4914],{"class":1912},[1736,10530,10531,10534,10537,10539],{"class":1738,"line":748},[1736,10532,10533],{"class":2674},"  render",[1736,10535,10536],{"class":1912},"(\u003C",[1736,10538,10354],{"class":1918},[1736,10540,10541],{"class":1912}," />)\n",[1736,10543,10544,10547,10549,10552,10554,10556,10559],{"class":1738,"line":756},[1736,10545,10546],{"class":1912},"  basicCounter ",[1736,10548,5062],{"class":4866},[1736,10550,10551],{"class":1912}," screen.",[1736,10553,1032],{"class":2674},[1736,10555,7751],{"class":1912},[1736,10557,10558],{"class":1935},"'spinbutton'",[1736,10560,7045],{"class":1912},[1736,10562,10563,10566,10569,10572,10574,10576],{"class":1738,"line":1755},[1736,10564,10565],{"class":2674},"  expect",[1736,10567,10568],{"class":1912},"(basicCounter).",[1736,10570,10571],{"class":2674},"toHaveValue",[1736,10573,7751],{"class":1912},[1736,10575,10249],{"class":1918},[1736,10577,7045],{"class":1912},[1736,10579,10580],{"class":1738,"line":1761},[1736,10581,10582],{"class":1912},"})\n",[11,10584,10585,10586,10588,10589,10591,10592,10594,10595,10598],{},"Our next test also needs to render the ",[179,10587,10354],{}," component as well as the the Button component with the name ",[179,10590,9343],{}," and we know that we will also have to render the Button component with the name ",[179,10593,9419],{}," in the last tests so we can go ahead and create a ",[179,10596,10597],{},"beforeEach"," function which will render these before each test is called.",[299,10600,10602],{"className":8734,"code":10601,"language":8736,"meta":307,"style":307},"let basicCounter\nlet increaseCount\nlet decreaseCount\n\nbeforeEach(() => {\n  render(\u003CBasicCounter />)\n  basicCounter = screen.getByRole('spinbutton')\n  increaseCount = screen.getByRole('button', { name: '+' })\n  decreaseCount = screen.getByRole('button', { name: '-' })\n})\n",[179,10603,10604,10612,10619,10626,10630,10641,10651,10667,10692,10714],{"__ignoreMap":307},[1736,10605,10606,10609],{"class":1738,"line":1739},[1736,10607,10608],{"class":4866},"let",[1736,10610,10611],{"class":1912}," basicCounter\n",[1736,10613,10614,10616],{"class":1738,"line":748},[1736,10615,10608],{"class":4866},[1736,10617,10618],{"class":1912}," increaseCount\n",[1736,10620,10621,10623],{"class":1738,"line":756},[1736,10622,10608],{"class":4866},[1736,10624,10625],{"class":1912}," decreaseCount\n",[1736,10627,10628],{"class":1738,"line":1755},[1736,10629,1747],{"emptyLinePlaceholder":790},[1736,10631,10632,10634,10637,10639],{"class":1738,"line":1761},[1736,10633,10597],{"class":2674},[1736,10635,10636],{"class":1912},"(() ",[1736,10638,7013],{"class":4866},[1736,10640,4914],{"class":1912},[1736,10642,10643,10645,10647,10649],{"class":1738,"line":1767},[1736,10644,10533],{"class":2674},[1736,10646,10536],{"class":1912},[1736,10648,10354],{"class":1918},[1736,10650,10541],{"class":1912},[1736,10652,10653,10655,10657,10659,10661,10663,10665],{"class":1738,"line":1772},[1736,10654,10546],{"class":1912},[1736,10656,5062],{"class":4866},[1736,10658,10551],{"class":1912},[1736,10660,1032],{"class":2674},[1736,10662,7751],{"class":1912},[1736,10664,10558],{"class":1935},[1736,10666,7045],{"class":1912},[1736,10668,10669,10672,10674,10676,10678,10680,10683,10686,10689],{"class":1738,"line":1778},[1736,10670,10671],{"class":1912},"  increaseCount ",[1736,10673,5062],{"class":4866},[1736,10675,10551],{"class":1912},[1736,10677,1032],{"class":2674},[1736,10679,7751],{"class":1912},[1736,10681,10682],{"class":1935},"'button'",[1736,10684,10685],{"class":1912},", { name: ",[1736,10687,10688],{"class":1935},"'+'",[1736,10690,10691],{"class":1912}," })\n",[1736,10693,10694,10697,10699,10701,10703,10705,10707,10709,10712],{"class":1738,"line":1784},[1736,10695,10696],{"class":1912},"  decreaseCount ",[1736,10698,5062],{"class":4866},[1736,10700,10551],{"class":1912},[1736,10702,1032],{"class":2674},[1736,10704,7751],{"class":1912},[1736,10706,10682],{"class":1935},[1736,10708,10685],{"class":1912},[1736,10710,10711],{"class":1935},"'-'",[1736,10713,10691],{"class":1912},[1736,10715,10716],{"class":1738,"line":1790},[1736,10717,10582],{"class":1912},[11,10719,10720],{},"Our first test will now look like this:",[299,10722,10724],{"className":8734,"code":10723,"language":8736,"meta":307,"style":307},"it('should render a counter with value of 1', () => {\n  expect(basicCounter).toHaveValue(1)\n})\n",[179,10725,10726,10740,10754],{"__ignoreMap":307},[1736,10727,10728,10730,10732,10734,10736,10738],{"class":1738,"line":1739},[1736,10729,10517],{"class":2674},[1736,10731,7751],{"class":1912},[1736,10733,10434],{"class":1935},[1736,10735,10524],{"class":1912},[1736,10737,7013],{"class":4866},[1736,10739,4914],{"class":1912},[1736,10741,10742,10744,10746,10748,10750,10752],{"class":1738,"line":748},[1736,10743,10565],{"class":2674},[1736,10745,10568],{"class":1912},[1736,10747,10571],{"class":2674},[1736,10749,7751],{"class":1912},[1736,10751,10249],{"class":1918},[1736,10753,7045],{"class":1912},[1736,10755,10756],{"class":1738,"line":756},[1736,10757,10582],{"class":1912},[11,10759,10760,10761,10763],{},"Our second test will make sure sure the value starts at 1 and when the button is clicked it will update to 2 using the ",[179,10762,10358],{}," method.",[299,10765,10767],{"className":8734,"code":10766,"language":8736,"meta":307,"style":307},"it('should increase count when plus button is clicked', () => {\n  expect(basicCounter).toHaveValue(1)\n  userEvent.click(increaseCount)\n  expect(basicCounter).toHaveValue(2)\n})\n",[179,10768,10769,10783,10797,10808,10823],{"__ignoreMap":307},[1736,10770,10771,10773,10775,10777,10779,10781],{"class":1738,"line":1739},[1736,10772,10517],{"class":2674},[1736,10774,7751],{"class":1912},[1736,10776,10451],{"class":1935},[1736,10778,10524],{"class":1912},[1736,10780,7013],{"class":4866},[1736,10782,4914],{"class":1912},[1736,10784,10785,10787,10789,10791,10793,10795],{"class":1738,"line":748},[1736,10786,10565],{"class":2674},[1736,10788,10568],{"class":1912},[1736,10790,10571],{"class":2674},[1736,10792,7751],{"class":1912},[1736,10794,10249],{"class":1918},[1736,10796,7045],{"class":1912},[1736,10798,10799,10802,10805],{"class":1738,"line":756},[1736,10800,10801],{"class":1912},"  userEvent.",[1736,10803,10804],{"class":2674},"click",[1736,10806,10807],{"class":1912},"(increaseCount)\n",[1736,10809,10810,10812,10814,10816,10818,10821],{"class":1738,"line":1755},[1736,10811,10565],{"class":2674},[1736,10813,10568],{"class":1912},[1736,10815,10571],{"class":2674},[1736,10817,7751],{"class":1912},[1736,10819,10820],{"class":1918},"2",[1736,10822,7045],{"class":1912},[1736,10824,10825],{"class":1738,"line":1761},[1736,10826,10582],{"class":1912},[11,10828,10829,10830,10832,10833,10835],{},"Our next test will start by calling the ",[179,10831,10358],{}," method to click the ",[179,10834,9343],{}," button and then make sure that the value is 2. Now we can test the decrease button by clicking that and making sure the value is back to 1.",[299,10837,10839],{"className":8734,"code":10838,"language":8736,"meta":307,"style":307},"it('should decrease count when minus button is clicked', () => {\n  userEvent.click(increaseCount)\n  expect(basicCounter).toHaveValue(2)\n  userEvent.click(decreaseCount)\n  expect(basicCounter).toHaveValue(1)\n})\n",[179,10840,10841,10855,10863,10877,10886,10900],{"__ignoreMap":307},[1736,10842,10843,10845,10847,10849,10851,10853],{"class":1738,"line":1739},[1736,10844,10517],{"class":2674},[1736,10846,7751],{"class":1912},[1736,10848,10468],{"class":1935},[1736,10850,10524],{"class":1912},[1736,10852,7013],{"class":4866},[1736,10854,4914],{"class":1912},[1736,10856,10857,10859,10861],{"class":1738,"line":748},[1736,10858,10801],{"class":1912},[1736,10860,10804],{"class":2674},[1736,10862,10807],{"class":1912},[1736,10864,10865,10867,10869,10871,10873,10875],{"class":1738,"line":756},[1736,10866,10565],{"class":2674},[1736,10868,10568],{"class":1912},[1736,10870,10571],{"class":2674},[1736,10872,7751],{"class":1912},[1736,10874,10820],{"class":1918},[1736,10876,7045],{"class":1912},[1736,10878,10879,10881,10883],{"class":1738,"line":1755},[1736,10880,10801],{"class":1912},[1736,10882,10804],{"class":2674},[1736,10884,10885],{"class":1912},"(decreaseCount)\n",[1736,10887,10888,10890,10892,10894,10896,10898],{"class":1738,"line":1761},[1736,10889,10565],{"class":2674},[1736,10891,10568],{"class":1912},[1736,10893,10571],{"class":2674},[1736,10895,7751],{"class":1912},[1736,10897,10249],{"class":1918},[1736,10899,7045],{"class":1912},[1736,10901,10902],{"class":1738,"line":1767},[1736,10903,10582],{"class":1912},[11,10905,10906],{},"Our last test will make sure that the value is not less than 1 and when the minus button is clicked it will still have the value of 1 as that is the min value allowed.",[299,10908,10910],{"className":8734,"code":10909,"language":8736,"meta":307,"style":307},"it('should not decrease to less than 1', () => {\n  expect(basicCounter).toHaveValue(1)\n  userEvent.click(decreaseCount)\n  expect(basicCounter).toHaveValue(1)\n})\n",[179,10911,10912,10926,10940,10948,10962],{"__ignoreMap":307},[1736,10913,10914,10916,10918,10920,10922,10924],{"class":1738,"line":1739},[1736,10915,10517],{"class":2674},[1736,10917,7751],{"class":1912},[1736,10919,10485],{"class":1935},[1736,10921,10524],{"class":1912},[1736,10923,7013],{"class":4866},[1736,10925,4914],{"class":1912},[1736,10927,10928,10930,10932,10934,10936,10938],{"class":1738,"line":748},[1736,10929,10565],{"class":2674},[1736,10931,10568],{"class":1912},[1736,10933,10571],{"class":2674},[1736,10935,7751],{"class":1912},[1736,10937,10249],{"class":1918},[1736,10939,7045],{"class":1912},[1736,10941,10942,10944,10946],{"class":1738,"line":756},[1736,10943,10801],{"class":1912},[1736,10945,10804],{"class":2674},[1736,10947,10885],{"class":1912},[1736,10949,10950,10952,10954,10956,10958,10960],{"class":1738,"line":1755},[1736,10951,10565],{"class":2674},[1736,10953,10568],{"class":1912},[1736,10955,10571],{"class":2674},[1736,10957,7751],{"class":1912},[1736,10959,10249],{"class":1918},[1736,10961,7045],{"class":1912},[1736,10963,10964],{"class":1738,"line":1761},[1736,10965,10582],{"class":1912},[11,10967,10968],{},"All our tests are passing and if we break anything in our component our tests will break as well.",[138,10970,10972],{"id":10971},"full-code-example","Full Code Example",[299,10974,10976],{"className":8734,"code":10975,"language":8736,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { BasicCounter } from './counter.composition'\n\nlet basicCounter\nlet increaseCount\nlet decreaseCount\n\nbeforeEach(() => {\n  render(\u003CBasicCounter />)\n  basicCounter = screen.getByRole('spinbutton')\n  increaseCount = screen.getByRole('button', { name: '+' })\n  decreaseCount = screen.getByRole('button', { name: '-' })\n})\n\nit('should render a counter with value of 1', () => {\n  expect(basicCounter).toHaveValue(1)\n})\n\nit('should increase count when plus button is clicked', () => {\n  expect(basicCounter).toHaveValue(1)\n  userEvent.click(increaseCount)\n  expect(basicCounter).toHaveValue(2)\n})\n\nit('should decrease count when minus button is clicked', () => {\n  userEvent.click(increaseCount)\n  expect(basicCounter).toHaveValue(2)\n  userEvent.click(decreaseCount)\n  expect(basicCounter).toHaveValue(1)\n})\n\nit('should not decrease to less than 1', () => {\n  expect(basicCounter).toHaveValue(1)\n  userEvent.click(decreaseCount)\n  expect(basicCounter).toHaveValue(1)\n})\n",[179,10977,10978,10988,10998,11008,11018,11022,11028,11034,11040,11044,11054,11064,11080,11100,11120,11124,11128,11142,11156,11160,11164,11178,11192,11200,11214,11218,11222,11236,11244,11258,11266,11280,11284,11288,11302,11316,11324,11338],{"__ignoreMap":307},[1736,10979,10980,10982,10984,10986],{"class":1738,"line":1739},[1736,10981,4996],{"class":4866},[1736,10983,6594],{"class":1912},[1736,10985,5002],{"class":4866},[1736,10987,6599],{"class":1935},[1736,10989,10990,10992,10994,10996],{"class":1738,"line":748},[1736,10991,4996],{"class":4866},[1736,10993,10384],{"class":1912},[1736,10995,5002],{"class":4866},[1736,10997,10389],{"class":1935},[1736,10999,11000,11002,11004,11006],{"class":1738,"line":756},[1736,11001,4996],{"class":4866},[1736,11003,10396],{"class":1912},[1736,11005,5002],{"class":4866},[1736,11007,10401],{"class":1935},[1736,11009,11010,11012,11014,11016],{"class":1738,"line":1755},[1736,11011,4996],{"class":4866},[1736,11013,10408],{"class":1912},[1736,11015,5002],{"class":4866},[1736,11017,10413],{"class":1935},[1736,11019,11020],{"class":1738,"line":1761},[1736,11021,1747],{"emptyLinePlaceholder":790},[1736,11023,11024,11026],{"class":1738,"line":1767},[1736,11025,10608],{"class":4866},[1736,11027,10611],{"class":1912},[1736,11029,11030,11032],{"class":1738,"line":1772},[1736,11031,10608],{"class":4866},[1736,11033,10618],{"class":1912},[1736,11035,11036,11038],{"class":1738,"line":1778},[1736,11037,10608],{"class":4866},[1736,11039,10625],{"class":1912},[1736,11041,11042],{"class":1738,"line":1784},[1736,11043,1747],{"emptyLinePlaceholder":790},[1736,11045,11046,11048,11050,11052],{"class":1738,"line":1790},[1736,11047,10597],{"class":2674},[1736,11049,10636],{"class":1912},[1736,11051,7013],{"class":4866},[1736,11053,4914],{"class":1912},[1736,11055,11056,11058,11060,11062],{"class":1738,"line":1796},[1736,11057,10533],{"class":2674},[1736,11059,10536],{"class":1912},[1736,11061,10354],{"class":1918},[1736,11063,10541],{"class":1912},[1736,11065,11066,11068,11070,11072,11074,11076,11078],{"class":1738,"line":2353},[1736,11067,10546],{"class":1912},[1736,11069,5062],{"class":4866},[1736,11071,10551],{"class":1912},[1736,11073,1032],{"class":2674},[1736,11075,7751],{"class":1912},[1736,11077,10558],{"class":1935},[1736,11079,7045],{"class":1912},[1736,11081,11082,11084,11086,11088,11090,11092,11094,11096,11098],{"class":1738,"line":2358},[1736,11083,10671],{"class":1912},[1736,11085,5062],{"class":4866},[1736,11087,10551],{"class":1912},[1736,11089,1032],{"class":2674},[1736,11091,7751],{"class":1912},[1736,11093,10682],{"class":1935},[1736,11095,10685],{"class":1912},[1736,11097,10688],{"class":1935},[1736,11099,10691],{"class":1912},[1736,11101,11102,11104,11106,11108,11110,11112,11114,11116,11118],{"class":1738,"line":2364},[1736,11103,10696],{"class":1912},[1736,11105,5062],{"class":4866},[1736,11107,10551],{"class":1912},[1736,11109,1032],{"class":2674},[1736,11111,7751],{"class":1912},[1736,11113,10682],{"class":1935},[1736,11115,10685],{"class":1912},[1736,11117,10711],{"class":1935},[1736,11119,10691],{"class":1912},[1736,11121,11122],{"class":1738,"line":2370},[1736,11123,10582],{"class":1912},[1736,11125,11126],{"class":1738,"line":2376},[1736,11127,1747],{"emptyLinePlaceholder":790},[1736,11129,11130,11132,11134,11136,11138,11140],{"class":1738,"line":2381},[1736,11131,10517],{"class":2674},[1736,11133,7751],{"class":1912},[1736,11135,10434],{"class":1935},[1736,11137,10524],{"class":1912},[1736,11139,7013],{"class":4866},[1736,11141,4914],{"class":1912},[1736,11143,11144,11146,11148,11150,11152,11154],{"class":1738,"line":2387},[1736,11145,10565],{"class":2674},[1736,11147,10568],{"class":1912},[1736,11149,10571],{"class":2674},[1736,11151,7751],{"class":1912},[1736,11153,10249],{"class":1918},[1736,11155,7045],{"class":1912},[1736,11157,11158],{"class":1738,"line":2393},[1736,11159,10582],{"class":1912},[1736,11161,11162],{"class":1738,"line":2398},[1736,11163,1747],{"emptyLinePlaceholder":790},[1736,11165,11166,11168,11170,11172,11174,11176],{"class":1738,"line":2404},[1736,11167,10517],{"class":2674},[1736,11169,7751],{"class":1912},[1736,11171,10451],{"class":1935},[1736,11173,10524],{"class":1912},[1736,11175,7013],{"class":4866},[1736,11177,4914],{"class":1912},[1736,11179,11180,11182,11184,11186,11188,11190],{"class":1738,"line":6959},[1736,11181,10565],{"class":2674},[1736,11183,10568],{"class":1912},[1736,11185,10571],{"class":2674},[1736,11187,7751],{"class":1912},[1736,11189,10249],{"class":1918},[1736,11191,7045],{"class":1912},[1736,11193,11194,11196,11198],{"class":1738,"line":7296},[1736,11195,10801],{"class":1912},[1736,11197,10804],{"class":2674},[1736,11199,10807],{"class":1912},[1736,11201,11202,11204,11206,11208,11210,11212],{"class":1738,"line":7305},[1736,11203,10565],{"class":2674},[1736,11205,10568],{"class":1912},[1736,11207,10571],{"class":2674},[1736,11209,7751],{"class":1912},[1736,11211,10820],{"class":1918},[1736,11213,7045],{"class":1912},[1736,11215,11216],{"class":1738,"line":7310},[1736,11217,10582],{"class":1912},[1736,11219,11220],{"class":1738,"line":9659},[1736,11221,1747],{"emptyLinePlaceholder":790},[1736,11223,11224,11226,11228,11230,11232,11234],{"class":1738,"line":9680},[1736,11225,10517],{"class":2674},[1736,11227,7751],{"class":1912},[1736,11229,10468],{"class":1935},[1736,11231,10524],{"class":1912},[1736,11233,7013],{"class":4866},[1736,11235,4914],{"class":1912},[1736,11237,11238,11240,11242],{"class":1738,"line":9699},[1736,11239,10801],{"class":1912},[1736,11241,10804],{"class":2674},[1736,11243,10807],{"class":1912},[1736,11245,11246,11248,11250,11252,11254,11256],{"class":1738,"line":9704},[1736,11247,10565],{"class":2674},[1736,11249,10568],{"class":1912},[1736,11251,10571],{"class":2674},[1736,11253,7751],{"class":1912},[1736,11255,10820],{"class":1918},[1736,11257,7045],{"class":1912},[1736,11259,11260,11262,11264],{"class":1738,"line":9715},[1736,11261,10801],{"class":1912},[1736,11263,10804],{"class":2674},[1736,11265,10885],{"class":1912},[1736,11267,11268,11270,11272,11274,11276,11278],{"class":1738,"line":9727},[1736,11269,10565],{"class":2674},[1736,11271,10568],{"class":1912},[1736,11273,10571],{"class":2674},[1736,11275,7751],{"class":1912},[1736,11277,10249],{"class":1918},[1736,11279,7045],{"class":1912},[1736,11281,11282],{"class":1738,"line":9739},[1736,11283,10582],{"class":1912},[1736,11285,11286],{"class":1738,"line":9750},[1736,11287,1747],{"emptyLinePlaceholder":790},[1736,11289,11290,11292,11294,11296,11298,11300],{"class":1738,"line":9761},[1736,11291,10517],{"class":2674},[1736,11293,7751],{"class":1912},[1736,11295,10485],{"class":1935},[1736,11297,10524],{"class":1912},[1736,11299,7013],{"class":4866},[1736,11301,4914],{"class":1912},[1736,11303,11304,11306,11308,11310,11312,11314],{"class":1738,"line":9767},[1736,11305,10565],{"class":2674},[1736,11307,10568],{"class":1912},[1736,11309,10571],{"class":2674},[1736,11311,7751],{"class":1912},[1736,11313,10249],{"class":1918},[1736,11315,7045],{"class":1912},[1736,11317,11318,11320,11322],{"class":1738,"line":9778},[1736,11319,10801],{"class":1912},[1736,11321,10804],{"class":2674},[1736,11323,10885],{"class":1912},[1736,11325,11326,11328,11330,11332,11334,11336],{"class":1738,"line":9799},[1736,11327,10565],{"class":2674},[1736,11329,10568],{"class":1912},[1736,11331,10571],{"class":2674},[1736,11333,7751],{"class":1912},[1736,11335,10249],{"class":1918},[1736,11337,7045],{"class":1912},[1736,11339,11340],{"class":1738,"line":9804},[1736,11341,10582],{"class":1912},[23,11343,3294],{"id":3293},[11,11345,11346],{},"And that's it. We now have a counter component that works as we would expect and can now be used in the component where it should be used knowing that it will work correctly. Compositions are a great way of seeing the different states of our components and we can then use the composition file to understand what we need to do to make our component work when using it in our next component/app.",[11,11348,11349],{},[121,11350],{"alt":11351,"src":11352},"counter component changing on click","https://res.cloudinary.com/debsobrien/image/upload/v1642011045/debbie.codes/blog/2022/counter-couting_zn9mcw.gif",[11,11354,11355],{},"We should also document our component so that it contains clear instructions and examples which makes it even easier for our consumer to understand what the component does and how to use it. And of course tests make sure our component not only works as expected but also that if we do make any changes to it our tests ensure that it can not be exported if our tests are broken meaning if we do have any breaking changes we can fix our tests and release a new major version of our component.",[23,11357,5706],{"id":5705},[70,11359,11360,11367,11373,11380,11387],{},[73,11361,11362],{},[15,11363,11366],{"href":11364,"rel":11365},"https://bit.dev/learn-bit-react/ecommerce/ui/product/counter",[19],"Counter Component",[73,11368,11369],{},[15,11370,11372],{"href":8693,"rel":11371},[19],"Counter Component Code",[73,11374,11375],{},[15,11376,11379],{"href":11377,"rel":11378},"https://bit.dev/learn-bit-react/ecommerce/ui/product/counter/~code/counter.composition.tsx",[19],"Counter Component Composition",[73,11381,11382],{},[15,11383,11386],{"href":11384,"rel":11385},"https://bit.dev/learn-bit-react/ecommerce/ui/product/counter/~code/counter.spec.tsx",[19],"Counter Component Tests",[73,11388,11389],{},[15,11390,11393],{"href":11391,"rel":11392},"https://testing-library.com/docs/react-testing-library/intro",[19],"React Testing Library",[2011,11395,11396],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":307,"searchDepth":748,"depth":748,"links":11398},[11399,11409,11410,11413,11414],{"id":8710,"depth":748,"text":8711,"children":11400},[11401,11402,11403,11404,11405,11406,11407,11408],{"id":8719,"depth":756,"text":8720},{"id":8787,"depth":756,"text":8788},{"id":8965,"depth":756,"text":8966},{"id":9255,"depth":756,"text":9256},{"id":9304,"depth":756,"text":9305},{"id":9368,"depth":756,"text":9369},{"id":9443,"depth":756,"text":9444},{"id":9498,"depth":756,"text":9499},{"id":10127,"depth":748,"text":10128},{"id":10333,"depth":748,"text":10334,"children":11411},[11412],{"id":10971,"depth":756,"text":10972},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2022-01-12","We want to build and test a counter component built with React and TypeScript. So where do we start? Which components do we need to build to create this component and what do we test?","v1642010879/debbie.codes/blog/2022/counter_2x_ma1ik6",{},"/blog/building-and-testing-counter-component",{"title":8685,"description":11416},"blog/building-and-testing-counter-component",[5221,1411],"gkuANc8MYcLFT39bGM8Vkv2DAe9sRl8766hS5EZJJOU",{"id":11425,"title":11426,"body":11427,"canonical":788,"date":13612,"description":13613,"extension":786,"featured":787,"image":13614,"meta":13615,"navigation":790,"ogimage":788,"path":13616,"provider":3460,"published":790,"seo":13617,"stem":13618,"tags":13619,"url":788,"__hash__":13620},"blog/blog/building-and-testing-select-input.md","Building and Testing a Select Component",{"type":8,"value":11428,"toc":13588},[11429,11457,11461,11466,11470,11473,11523,11527,11542,11632,11636,11643,11689,11693,11709,11782,11786,11814,11827,11986,11990,12003,12039,12043,12046,12395,12397,12401,12404,12426,12441,12453,12462,12673,12676,12678,12682,12685,12689,12699,12746,12750,12753,12818,12822,12825,12830,12907,12911,12918,13014,13018,13033,13152,13156,13168,13295,13299,13322,13476,13478,13480,13483,13489,13491,13495,13502,13540,13542,13585],[11,11430,11431,11432,11437,11438,11443,11444,11451,11452,11456],{},"I have created a component that is actually made up of 2 smaller components, a ",[15,11433,11436],{"href":11434,"rel":11435},"https://bit.dev/learn-bit-react/base-ui/ui/forms/select",[19],"Select"," element and a ",[15,11439,11442],{"href":11440,"rel":11441},"https://bit.dev/learn-bit-react/base-ui/ui/forms/label",[19],"Label",". Together these two components make up a component I have named ",[15,11445,11448],{"href":11446,"rel":11447},"https://bit.dev/learn-bit-react/ecommerce/ui/product/select-size",[19],[179,11449,11450],{},"select-size"," and is the component used in the ",[15,11453,11455],{"href":4271,"rel":11454},[19],"demo e-commerce project"," I have created in order for users to select the size of the product.",[23,11458,11460],{"id":11459},"building-the-select-component","Building the Select Component",[11,11462,11463],{},[121,11464],{"alt":11450,"src":11465},"https://res.cloudinary.com/debsobrien/image/upload/v1641812664/debbie.codes/blog/2022/select_2x_g2mbjj",[138,11467,11469],{"id":11468},"importing-react-testing-library-and-components","Importing React, Testing Library and Components",[11,11471,11472],{},"The component is built in React and TypeScript and imports React, useState and the two components needed to build this component as well as the styles.",[299,11474,11476],{"className":4894,"code":11475,"language":4896,"meta":307,"style":307},"import React, { useState } from 'react'\nimport { Select } from '@learn-bit-react/base-ui.ui.forms.select'\nimport { Label } from '@learn-bit-react/base-ui.ui.forms.label'\nimport styles from './select-size.module.scss'\n",[179,11477,11478,11488,11500,11512],{"__ignoreMap":307},[1736,11479,11480,11482,11484,11486],{"class":1738,"line":1739},[1736,11481,4996],{"class":4866},[1736,11483,8745],{"class":1912},[1736,11485,5002],{"class":4866},[1736,11487,6599],{"class":1935},[1736,11489,11490,11492,11495,11497],{"class":1738,"line":748},[1736,11491,4996],{"class":4866},[1736,11493,11494],{"class":1912}," { Select } ",[1736,11496,5002],{"class":4866},[1736,11498,11499],{"class":1935}," '@learn-bit-react/base-ui.ui.forms.select'\n",[1736,11501,11502,11504,11507,11509],{"class":1738,"line":756},[1736,11503,4996],{"class":4866},[1736,11505,11506],{"class":1912}," { Label } ",[1736,11508,5002],{"class":4866},[1736,11510,11511],{"class":1935}," '@learn-bit-react/base-ui.ui.forms.label'\n",[1736,11513,11514,11516,11518,11520],{"class":1738,"line":1755},[1736,11515,4996],{"class":4866},[1736,11517,6618],{"class":1912},[1736,11519,5002],{"class":4866},[1736,11521,11522],{"class":1935}," './select-size.module.scss'\n",[138,11524,11526],{"id":11525},"prop-types","Prop Types",[11,11528,11529,11530,11533,11534,11537,11538,11541],{},"The props being passed down are the ",[179,11531,11532],{},"availableSizes"," which is an array of numbers and the ",[179,11535,11536],{},"sizeSelected"," which is a function that passes in the ",[179,11539,11540],{},"size"," of the product. As we are using Typescript we first export our types, this ensures our user can only use the types specified such as the array of available sizes can only be a number and not a string.",[299,11543,11545],{"className":4894,"code":11544,"language":4896,"meta":307,"style":307},"export type SelectSizeProps = {\n  /**\n   * sizes as an array of numbers\n   */\n  availableSizes: number[],\n  /**\n   * a function that registers the selected size.\n   */\n  sizeSelected: size => void\n} & React.SelectHTMLAttributes\u003CHTMLSelectElement>\n",[179,11546,11547,11560,11564,11569,11573,11585,11589,11594,11598,11612],{"__ignoreMap":307},[1736,11548,11549,11551,11553,11556,11558],{"class":1738,"line":1739},[1736,11550,6632],{"class":4866},[1736,11552,6635],{"class":4866},[1736,11554,11555],{"class":2674}," SelectSizeProps",[1736,11557,4911],{"class":4866},[1736,11559,4914],{"class":1912},[1736,11561,11562],{"class":1738,"line":748},[1736,11563,6821],{"class":6820},[1736,11565,11566],{"class":1738,"line":756},[1736,11567,11568],{"class":6820},"   * sizes as an array of numbers\n",[1736,11570,11571],{"class":1738,"line":1755},[1736,11572,6831],{"class":6820},[1736,11574,11575,11578,11580,11582],{"class":1738,"line":1761},[1736,11576,11577],{"class":5036},"  availableSizes",[1736,11579,1087],{"class":4866},[1736,11581,8833],{"class":1918},[1736,11583,11584],{"class":1912},"[],\n",[1736,11586,11587],{"class":1738,"line":1767},[1736,11588,6821],{"class":6820},[1736,11590,11591],{"class":1738,"line":1772},[1736,11592,11593],{"class":6820},"   * a function that registers the selected size.\n",[1736,11595,11596],{"class":1738,"line":1778},[1736,11597,6831],{"class":6820},[1736,11599,11600,11603,11605,11608,11610],{"class":1738,"line":1784},[1736,11601,11602],{"class":5036},"  sizeSelected",[1736,11604,1087],{"class":4866},[1736,11606,11607],{"class":2674}," size",[1736,11609,10208],{"class":4866},[1736,11611,8944],{"class":1918},[1736,11613,11614,11616,11618,11620,11622,11625,11627,11630],{"class":1738,"line":1790},[1736,11615,6871],{"class":1912},[1736,11617,6646],{"class":4866},[1736,11619,6649],{"class":2674},[1736,11621,891],{"class":1912},[1736,11623,11624],{"class":2674},"SelectHTMLAttributes",[1736,11626,6657],{"class":1912},[1736,11628,11629],{"class":2674},"HTMLSelectElement",[1736,11631,6663],{"class":1912},[138,11633,11635],{"id":11634},"passing-down-props","Passing down Props",[11,11637,11638,11639,11642],{},"We then pass down the props into our SelectSize component as well as ",[179,11640,11641],{},"...rest"," which gives access to all other props that a html select element can have.",[299,11644,11646],{"className":4894,"code":11645,"language":4896,"meta":307,"style":307},"export function SelectSize({\n  availableSizes,\n  sizeSelected,\n  ...rest\n}: SelectSizeProps) {}\n",[179,11647,11648,11659,11665,11671,11678],{"__ignoreMap":307},[1736,11649,11650,11652,11654,11657],{"class":1738,"line":1739},[1736,11651,6632],{"class":4866},[1736,11653,6674],{"class":4866},[1736,11655,11656],{"class":2674}," SelectSize",[1736,11658,5122],{"class":1912},[1736,11660,11661,11663],{"class":1738,"line":748},[1736,11662,11577],{"class":5036},[1736,11664,1939],{"class":1912},[1736,11666,11667,11669],{"class":1738,"line":756},[1736,11668,11602],{"class":5036},[1736,11670,1939],{"class":1912},[1736,11672,11673,11675],{"class":1738,"line":1755},[1736,11674,8441],{"class":4866},[1736,11676,11677],{"class":5036},"rest\n",[1736,11679,11680,11682,11684,11686],{"class":1738,"line":1761},[1736,11681,9053],{"class":1912},[1736,11683,1087],{"class":4866},[1736,11685,11555],{"class":2674},[1736,11687,11688],{"class":1912},") {}\n",[138,11690,11692],{"id":11691},"adding-state","Adding State",[11,11694,11695,11696,11698,11699,11701,11702,11705,11706,11708],{},"Our component uses the ",[179,11697,9245],{}," hook to set the size of the product. ",[179,11700,11540],{}," is the value of the select element and ",[179,11703,11704],{},"setSize"," is the function that allows us to set a new value. The default state will be the first number of the ",[179,11707,11532],{}," array.",[299,11710,11712],{"className":4894,"code":11711,"language":4896,"meta":307,"style":307},"export function SelectSize({\n  availableSizes,\n  sizeSelected,\n  ...rest\n}: SelectSizeProps) {\n  const [size, setSize] = useState(availableSizes[0])\n}\n",[179,11713,11714,11724,11730,11736,11742,11752,11778],{"__ignoreMap":307},[1736,11715,11716,11718,11720,11722],{"class":1738,"line":1739},[1736,11717,6632],{"class":4866},[1736,11719,6674],{"class":4866},[1736,11721,11656],{"class":2674},[1736,11723,5122],{"class":1912},[1736,11725,11726,11728],{"class":1738,"line":748},[1736,11727,11577],{"class":5036},[1736,11729,1939],{"class":1912},[1736,11731,11732,11734],{"class":1738,"line":756},[1736,11733,11602],{"class":5036},[1736,11735,1939],{"class":1912},[1736,11737,11738,11740],{"class":1738,"line":1755},[1736,11739,8441],{"class":4866},[1736,11741,11677],{"class":5036},[1736,11743,11744,11746,11748,11750],{"class":1738,"line":1761},[1736,11745,9053],{"class":1912},[1736,11747,1087],{"class":4866},[1736,11749,11555],{"class":2674},[1736,11751,7246],{"class":1912},[1736,11753,11754,11756,11758,11760,11762,11764,11766,11768,11770,11773,11775],{"class":1738,"line":1767},[1736,11755,7824],{"class":4866},[1736,11757,8409],{"class":1912},[1736,11759,11540],{"class":1918},[1736,11761,829],{"class":1912},[1736,11763,11704],{"class":1918},[1736,11765,9277],{"class":1912},[1736,11767,5062],{"class":4866},[1736,11769,9282],{"class":2674},[1736,11771,11772],{"class":1912},"(availableSizes[",[1736,11774,1290],{"class":1918},[1736,11776,11777],{"class":1912},"])\n",[1736,11779,11780],{"class":1738,"line":1772},[1736,11781,1976],{"class":1912},[138,11783,11785],{"id":11784},"using-the-select-and-label-components","Using the Select and Label Components",[11,11787,11788,11789,11793,11794,11797,11798,11800,11801,11805,11806,11808,11809,11811,11812,11708],{},"We can now add the return statement to our component and return the Select and Label components that we have imported. The ",[15,11790,11792],{"href":11440,"rel":11791},[19],"Label component"," is pretty straight forward and just adds some styles and the ",[179,11795,11796],{},"htmlFor"," attribute with the value of ",[179,11799,11540],{},". For the ",[15,11802,11804],{"href":11434,"rel":11803},[19],"Select component"," we need to add the ",[179,11807,7167],{}," of ",[179,11810,11540],{},", the className for the styles, and the options for the select component which is equal to the value of the ",[179,11813,11532],{},[11,11815,11816,11817,11820,11821,11823,11824,11826],{},"The Select component takes in a prop of options and will map over the array to give us an ",[179,11818,11819],{},"\u003Coption>"," for each number in the array. We then need an ",[179,11822,9008],{}," function to handle the change for every time the user changes the size. And of course we pass in the ",[179,11825,11641],{}," of the props that a html select element can take.",[299,11828,11830],{"className":4894,"code":11829,"language":4896,"meta":307,"style":307},"const [size, setSize] = useState(availableSizes[0])\nreturn (\n  \u003Cdiv className={styles.selectSize}>\n    \u003CLabel className={styles.label} htmlFor=\"size\">\n      Choose a size:\n    \u003C/Label>\n    \u003CSelect\n      id=\"size\"\n      className={styles.select}\n      options={availableSizes}\n      onChange={handleChange}\n      {...rest}\n    />\n  \u003C/div>\n)\n",[179,11831,11832,11856,11863,11876,11898,11903,11911,11918,11928,11938,11948,11958,11969,11974,11982],{"__ignoreMap":307},[1736,11833,11834,11836,11838,11840,11842,11844,11846,11848,11850,11852,11854],{"class":1738,"line":1739},[1736,11835,5029],{"class":4866},[1736,11837,8409],{"class":1912},[1736,11839,11540],{"class":1918},[1736,11841,829],{"class":1912},[1736,11843,11704],{"class":1918},[1736,11845,9277],{"class":1912},[1736,11847,5062],{"class":4866},[1736,11849,9282],{"class":2674},[1736,11851,11772],{"class":1912},[1736,11853,1290],{"class":1918},[1736,11855,11777],{"class":1912},[1736,11857,11858,11861],{"class":1738,"line":748},[1736,11859,11860],{"class":4866},"return",[1736,11862,6688],{"class":1912},[1736,11864,11865,11867,11869,11871,11873],{"class":1738,"line":756},[1736,11866,7020],{"class":1912},[1736,11868,6697],{"class":6696},[1736,11870,6700],{"class":2674},[1736,11872,5062],{"class":4866},[1736,11874,11875],{"class":1912},"{styles.selectSize}>\n",[1736,11877,11878,11880,11882,11884,11886,11889,11891,11893,11896],{"class":1738,"line":1755},[1736,11879,6693],{"class":1912},[1736,11881,11442],{"class":1918},[1736,11883,6700],{"class":2674},[1736,11885,5062],{"class":4866},[1736,11887,11888],{"class":1912},"{styles.label} ",[1736,11890,11796],{"class":2674},[1736,11892,5062],{"class":4866},[1736,11894,11895],{"class":1935},"\"size\"",[1736,11897,6663],{"class":1912},[1736,11899,11900],{"class":1738,"line":1761},[1736,11901,11902],{"class":1912},"      Choose a size:\n",[1736,11904,11905,11907,11909],{"class":1738,"line":1767},[1736,11906,6744],{"class":1912},[1736,11908,11442],{"class":1918},[1736,11910,6663],{"class":1912},[1736,11912,11913,11915],{"class":1738,"line":1772},[1736,11914,6693],{"class":1912},[1736,11916,11917],{"class":1918},"Select\n",[1736,11919,11920,11923,11925],{"class":1738,"line":1778},[1736,11921,11922],{"class":2674},"      id",[1736,11924,5062],{"class":4866},[1736,11926,11927],{"class":1935},"\"size\"\n",[1736,11929,11930,11933,11935],{"class":1738,"line":1784},[1736,11931,11932],{"class":2674},"      className",[1736,11934,5062],{"class":4866},[1736,11936,11937],{"class":1912},"{styles.select}\n",[1736,11939,11940,11943,11945],{"class":1738,"line":1790},[1736,11941,11942],{"class":2674},"      options",[1736,11944,5062],{"class":4866},[1736,11946,11947],{"class":1912},"{availableSizes}\n",[1736,11949,11950,11953,11955],{"class":1738,"line":1796},[1736,11951,11952],{"class":2674},"      onChange",[1736,11954,5062],{"class":4866},[1736,11956,11957],{"class":1912},"{handleChange}\n",[1736,11959,11960,11963,11966],{"class":1738,"line":2353},[1736,11961,11962],{"class":1912},"      {",[1736,11964,11965],{"class":4866},"...",[1736,11967,11968],{"class":1912},"rest}\n",[1736,11970,11971],{"class":1738,"line":2358},[1736,11972,11973],{"class":1912},"    />\n",[1736,11975,11976,11978,11980],{"class":1738,"line":2364},[1736,11977,8096],{"class":1912},[1736,11979,6697],{"class":6696},[1736,11981,6663],{"class":1912},[1736,11983,11984],{"class":1738,"line":2370},[1736,11985,7045],{"class":1912},[138,11987,11989],{"id":11988},"creating-the-handlechange-function","Creating the handleChange function",[11,11991,11992,11993,11996,11997,11999,12000,12002],{},"We can now create our ",[179,11994,11995],{},"handleChange"," function which will set the state of ",[179,11998,11540],{}," to be the value of the select element as well as call the ",[179,12001,11536],{}," function with the value of the select element.",[299,12004,12006],{"className":4894,"code":12005,"language":4896,"meta":307,"style":307},"function handleChange(e) {\n  setSize(e.target.value)\n  sizeSelected(e.target.value)\n}\n",[179,12007,12008,12021,12029,12035],{"__ignoreMap":307},[1736,12009,12010,12012,12015,12017,12019],{"class":1738,"line":1739},[1736,12011,7745],{"class":4866},[1736,12013,12014],{"class":2674}," handleChange",[1736,12016,7751],{"class":1912},[1736,12018,9475],{"class":5036},[1736,12020,7246],{"class":1912},[1736,12022,12023,12026],{"class":1738,"line":748},[1736,12024,12025],{"class":2674},"  setSize",[1736,12027,12028],{"class":1912},"(e.target.value)\n",[1736,12030,12031,12033],{"class":1738,"line":756},[1736,12032,11602],{"class":2674},[1736,12034,12028],{"class":1912},[1736,12036,12037],{"class":1738,"line":1755},[1736,12038,1976],{"class":1912},[138,12040,12042],{"id":12041},"final-code","Final Code",[11,12044,12045],{},"The full code for our component will look like this:",[299,12047,12049],{"className":4894,"code":12048,"language":4896,"meta":307,"style":307},"import React, { useState } from 'react'\nimport { Select } from '@learn-bit-react/base-ui.ui.forms.select'\nimport { Label } from '@learn-bit-react/base-ui.ui.forms.label'\nimport styles from './select-size.module.scss'\n\nexport type SelectSizeProps = {\n  /**\n   * sizes as an array of numbers\n   */\n  availableSizes: number[],\n  /**\n   * a function that registers the selected size.\n   */\n  sizeSelected: size => void\n} & React.SelectHTMLAttributes\u003CHTMLSelectElement>\n\nexport function SelectSize({\n  availableSizes,\n  sizeSelected,\n  ...rest\n}: SelectSizeProps) {\n  const [size, setSize] = useState(availableSizes[0])\n\n  function handleChange(e) {\n    setSize(e.target.value)\n    sizeSelected(e.target.value)\n  }\n\n  return (\n    \u003Cdiv className={styles.selectSize}>\n      \u003CLabel className={styles.label} htmlFor=\"size\">\n        Choose a size:\n      \u003C/Label>\n      \u003CSelect\n        id=\"size\"\n        className={styles.select}\n        options={availableSizes}\n        onChange={handleChange}\n        {...rest}\n      />\n    \u003C/div>\n  )\n}\n",[179,12050,12051,12061,12071,12081,12091,12095,12107,12111,12115,12119,12129,12133,12137,12141,12153,12171,12175,12185,12191,12197,12203,12213,12237,12241,12253,12260,12267,12271,12275,12281,12293,12313,12318,12326,12332,12341,12349,12358,12366,12375,12379,12387,12391],{"__ignoreMap":307},[1736,12052,12053,12055,12057,12059],{"class":1738,"line":1739},[1736,12054,4996],{"class":4866},[1736,12056,8745],{"class":1912},[1736,12058,5002],{"class":4866},[1736,12060,6599],{"class":1935},[1736,12062,12063,12065,12067,12069],{"class":1738,"line":748},[1736,12064,4996],{"class":4866},[1736,12066,11494],{"class":1912},[1736,12068,5002],{"class":4866},[1736,12070,11499],{"class":1935},[1736,12072,12073,12075,12077,12079],{"class":1738,"line":756},[1736,12074,4996],{"class":4866},[1736,12076,11506],{"class":1912},[1736,12078,5002],{"class":4866},[1736,12080,11511],{"class":1935},[1736,12082,12083,12085,12087,12089],{"class":1738,"line":1755},[1736,12084,4996],{"class":4866},[1736,12086,6618],{"class":1912},[1736,12088,5002],{"class":4866},[1736,12090,11522],{"class":1935},[1736,12092,12093],{"class":1738,"line":1761},[1736,12094,1747],{"emptyLinePlaceholder":790},[1736,12096,12097,12099,12101,12103,12105],{"class":1738,"line":1767},[1736,12098,6632],{"class":4866},[1736,12100,6635],{"class":4866},[1736,12102,11555],{"class":2674},[1736,12104,4911],{"class":4866},[1736,12106,4914],{"class":1912},[1736,12108,12109],{"class":1738,"line":1772},[1736,12110,6821],{"class":6820},[1736,12112,12113],{"class":1738,"line":1778},[1736,12114,11568],{"class":6820},[1736,12116,12117],{"class":1738,"line":1784},[1736,12118,6831],{"class":6820},[1736,12120,12121,12123,12125,12127],{"class":1738,"line":1790},[1736,12122,11577],{"class":5036},[1736,12124,1087],{"class":4866},[1736,12126,8833],{"class":1918},[1736,12128,11584],{"class":1912},[1736,12130,12131],{"class":1738,"line":1796},[1736,12132,6821],{"class":6820},[1736,12134,12135],{"class":1738,"line":2353},[1736,12136,11593],{"class":6820},[1736,12138,12139],{"class":1738,"line":2358},[1736,12140,6831],{"class":6820},[1736,12142,12143,12145,12147,12149,12151],{"class":1738,"line":2364},[1736,12144,11602],{"class":5036},[1736,12146,1087],{"class":4866},[1736,12148,11607],{"class":2674},[1736,12150,10208],{"class":4866},[1736,12152,8944],{"class":1918},[1736,12154,12155,12157,12159,12161,12163,12165,12167,12169],{"class":1738,"line":2370},[1736,12156,6871],{"class":1912},[1736,12158,6646],{"class":4866},[1736,12160,6649],{"class":2674},[1736,12162,891],{"class":1912},[1736,12164,11624],{"class":2674},[1736,12166,6657],{"class":1912},[1736,12168,11629],{"class":2674},[1736,12170,6663],{"class":1912},[1736,12172,12173],{"class":1738,"line":2376},[1736,12174,1747],{"emptyLinePlaceholder":790},[1736,12176,12177,12179,12181,12183],{"class":1738,"line":2381},[1736,12178,6632],{"class":4866},[1736,12180,6674],{"class":4866},[1736,12182,11656],{"class":2674},[1736,12184,5122],{"class":1912},[1736,12186,12187,12189],{"class":1738,"line":2387},[1736,12188,11577],{"class":5036},[1736,12190,1939],{"class":1912},[1736,12192,12193,12195],{"class":1738,"line":2393},[1736,12194,11602],{"class":5036},[1736,12196,1939],{"class":1912},[1736,12198,12199,12201],{"class":1738,"line":2398},[1736,12200,8441],{"class":4866},[1736,12202,11677],{"class":5036},[1736,12204,12205,12207,12209,12211],{"class":1738,"line":2404},[1736,12206,9053],{"class":1912},[1736,12208,1087],{"class":4866},[1736,12210,11555],{"class":2674},[1736,12212,7246],{"class":1912},[1736,12214,12215,12217,12219,12221,12223,12225,12227,12229,12231,12233,12235],{"class":1738,"line":6959},[1736,12216,7824],{"class":4866},[1736,12218,8409],{"class":1912},[1736,12220,11540],{"class":1918},[1736,12222,829],{"class":1912},[1736,12224,11704],{"class":1918},[1736,12226,9277],{"class":1912},[1736,12228,5062],{"class":4866},[1736,12230,9282],{"class":2674},[1736,12232,11772],{"class":1912},[1736,12234,1290],{"class":1918},[1736,12236,11777],{"class":1912},[1736,12238,12239],{"class":1738,"line":7296},[1736,12240,1747],{"emptyLinePlaceholder":790},[1736,12242,12243,12245,12247,12249,12251],{"class":1738,"line":7305},[1736,12244,9807],{"class":4866},[1736,12246,12014],{"class":2674},[1736,12248,7751],{"class":1912},[1736,12250,9475],{"class":5036},[1736,12252,7246],{"class":1912},[1736,12254,12255,12258],{"class":1738,"line":7310},[1736,12256,12257],{"class":2674},"    setSize",[1736,12259,12028],{"class":1912},[1736,12261,12262,12265],{"class":1738,"line":9659},[1736,12263,12264],{"class":2674},"    sizeSelected",[1736,12266,12028],{"class":1912},[1736,12268,12269],{"class":1738,"line":9680},[1736,12270,1971],{"class":1912},[1736,12272,12273],{"class":1738,"line":9699},[1736,12274,1747],{"emptyLinePlaceholder":790},[1736,12276,12277,12279],{"class":1738,"line":9704},[1736,12278,6685],{"class":4866},[1736,12280,6688],{"class":1912},[1736,12282,12283,12285,12287,12289,12291],{"class":1738,"line":9715},[1736,12284,6693],{"class":1912},[1736,12286,6697],{"class":6696},[1736,12288,6700],{"class":2674},[1736,12290,5062],{"class":4866},[1736,12292,11875],{"class":1912},[1736,12294,12295,12297,12299,12301,12303,12305,12307,12309,12311],{"class":1738,"line":9727},[1736,12296,6710],{"class":1912},[1736,12298,11442],{"class":1918},[1736,12300,6700],{"class":2674},[1736,12302,5062],{"class":4866},[1736,12304,11888],{"class":1912},[1736,12306,11796],{"class":2674},[1736,12308,5062],{"class":4866},[1736,12310,11895],{"class":1935},[1736,12312,6663],{"class":1912},[1736,12314,12315],{"class":1738,"line":9739},[1736,12316,12317],{"class":1912},"        Choose a size:\n",[1736,12319,12320,12322,12324],{"class":1738,"line":9750},[1736,12321,8261],{"class":1912},[1736,12323,11442],{"class":1918},[1736,12325,6663],{"class":1912},[1736,12327,12328,12330],{"class":1738,"line":9761},[1736,12329,6710],{"class":1912},[1736,12331,11917],{"class":1918},[1736,12333,12334,12337,12339],{"class":1738,"line":9767},[1736,12335,12336],{"class":2674},"        id",[1736,12338,5062],{"class":4866},[1736,12340,11927],{"class":1935},[1736,12342,12343,12345,12347],{"class":1738,"line":9778},[1736,12344,9125],{"class":2674},[1736,12346,5062],{"class":4866},[1736,12348,11937],{"class":1912},[1736,12350,12351,12354,12356],{"class":1738,"line":9799},[1736,12352,12353],{"class":2674},"        options",[1736,12355,5062],{"class":4866},[1736,12357,11947],{"class":1912},[1736,12359,12360,12362,12364],{"class":1738,"line":9804},[1736,12361,9185],{"class":2674},[1736,12363,5062],{"class":4866},[1736,12365,11957],{"class":1912},[1736,12367,12368,12371,12373],{"class":1738,"line":9814},[1736,12369,12370],{"class":1912},"        {",[1736,12372,11965],{"class":4866},[1736,12374,11968],{"class":1912},[1736,12376,12377],{"class":1738,"line":9826},[1736,12378,9195],{"class":1912},[1736,12380,12381,12383,12385],{"class":1738,"line":9838},[1736,12382,6744],{"class":1912},[1736,12384,6697],{"class":6696},[1736,12386,6663],{"class":1912},[1736,12388,12389],{"class":1738,"line":9850},[1736,12390,6753],{"class":1912},[1736,12392,12393],{"class":1738,"line":9856},[1736,12394,1976],{"class":1912},[731,12396],{},[23,12398,12400],{"id":12399},"creating-compositions","Creating Compositions",[11,12402,12403],{},"We now need to make compositions for our component to see the component in action. Compositions are a feature of Bit that allows you to see your component in isolation. If not using Bit then you can create mocks to test your component.",[11,12405,12406,12407,12410,12411,12414,12415,12418,12419,12421,12422,12425],{},"My compositions imports React and useState as well as the component we just created. We then create a ",[179,12408,12409],{},"SelectSizeAndShowSelectedSize"," component that will render the ",[179,12412,12413],{},"SelectSize"," component. We first create a const of ",[179,12416,12417],{},"sizes"," equal to an array of numbers, the sizes the want to show. We then use the ",[179,12420,9245],{}," hook to set the state of the ",[179,12423,12424],{},"selectedSize"," giving it the default of the first value from our sizes array.",[11,12427,12428,12429,12431,12432,12434,12435,12437,12438,12440],{},"Then in our component we make the prop of ",[179,12430,11536],{}," equal to a function that passes in the argument of ",[179,12433,11540],{}," and sets the state of ",[179,12436,12424],{}," to be the value of ",[179,12439,11540],{},". This give us access to the value of the size selected so as we can use it in another component.",[11,12442,12443,12444,12446,12447,12449,12450,12452],{},"We also add the value of our ",[179,12445,12417],{}," array to the ",[179,12448,11532],{}," prop of the ",[179,12451,12413],{}," component.",[11,12454,12455,12456,12458,12459,12461],{},"And finally we add a ",[179,12457,10137],{}," tag with the value of the ",[179,12460,12424],{}," so we can see the size of the product updated in the UI as we change it.",[299,12463,12465],{"className":4894,"code":12464,"language":4896,"meta":307,"style":307},"import React, { useState } from 'react'\nimport { SelectSize } from './select-size'\n\nexport function SelectSizeAndShowSelectedSize() {\n  const sizes = [36, 37, 38, 39, 40, 45, 46, 47]\n  const [selectedSize, setSelectedSize] = useState(sizes[0])\n\n  return (\n    \u003C>\n      \u003CSelectSize\n        sizeSelected={size => {\n          setSelectedSize(parseInt(size))\n        }}\n        availableSizes={sizes}\n      />\n      \u003Cp>You're selected size is: {selectedSize}\u003C/p>\n    \u003C/>\n  )\n}\n",[179,12466,12467,12477,12489,12493,12504,12555,12581,12585,12591,12595,12602,12617,12630,12634,12644,12648,12661,12665,12669],{"__ignoreMap":307},[1736,12468,12469,12471,12473,12475],{"class":1738,"line":1739},[1736,12470,4996],{"class":4866},[1736,12472,8745],{"class":1912},[1736,12474,5002],{"class":4866},[1736,12476,6599],{"class":1935},[1736,12478,12479,12481,12484,12486],{"class":1738,"line":748},[1736,12480,4996],{"class":4866},[1736,12482,12483],{"class":1912}," { SelectSize } ",[1736,12485,5002],{"class":4866},[1736,12487,12488],{"class":1935}," './select-size'\n",[1736,12490,12491],{"class":1738,"line":756},[1736,12492,1747],{"emptyLinePlaceholder":790},[1736,12494,12495,12497,12499,12502],{"class":1738,"line":1755},[1736,12496,6632],{"class":4866},[1736,12498,6674],{"class":4866},[1736,12500,12501],{"class":2674}," SelectSizeAndShowSelectedSize",[1736,12503,6680],{"class":1912},[1736,12505,12506,12508,12511,12513,12515,12518,12520,12523,12525,12528,12530,12533,12535,12538,12540,12543,12545,12548,12550,12553],{"class":1738,"line":1761},[1736,12507,7824],{"class":4866},[1736,12509,12510],{"class":1918}," sizes",[1736,12512,4911],{"class":4866},[1736,12514,8409],{"class":1912},[1736,12516,12517],{"class":1918},"36",[1736,12519,829],{"class":1912},[1736,12521,12522],{"class":1918},"37",[1736,12524,829],{"class":1912},[1736,12526,12527],{"class":1918},"38",[1736,12529,829],{"class":1912},[1736,12531,12532],{"class":1918},"39",[1736,12534,829],{"class":1912},[1736,12536,12537],{"class":1918},"40",[1736,12539,829],{"class":1912},[1736,12541,12542],{"class":1918},"45",[1736,12544,829],{"class":1912},[1736,12546,12547],{"class":1918},"46",[1736,12549,829],{"class":1912},[1736,12551,12552],{"class":1918},"47",[1736,12554,8420],{"class":1912},[1736,12556,12557,12559,12561,12563,12565,12568,12570,12572,12574,12577,12579],{"class":1738,"line":1767},[1736,12558,7824],{"class":4866},[1736,12560,8409],{"class":1912},[1736,12562,12424],{"class":1918},[1736,12564,829],{"class":1912},[1736,12566,12567],{"class":1918},"setSelectedSize",[1736,12569,9277],{"class":1912},[1736,12571,5062],{"class":4866},[1736,12573,9282],{"class":2674},[1736,12575,12576],{"class":1912},"(sizes[",[1736,12578,1290],{"class":1918},[1736,12580,11777],{"class":1912},[1736,12582,12583],{"class":1738,"line":1772},[1736,12584,1747],{"emptyLinePlaceholder":790},[1736,12586,12587,12589],{"class":1738,"line":1778},[1736,12588,6685],{"class":4866},[1736,12590,6688],{"class":1912},[1736,12592,12593],{"class":1738,"line":1784},[1736,12594,10262],{"class":1912},[1736,12596,12597,12599],{"class":1738,"line":1790},[1736,12598,6710],{"class":1912},[1736,12600,12601],{"class":1918},"SelectSize\n",[1736,12603,12604,12607,12609,12611,12613,12615],{"class":1738,"line":1796},[1736,12605,12606],{"class":2674},"        sizeSelected",[1736,12608,5062],{"class":4866},[1736,12610,7387],{"class":1912},[1736,12612,11540],{"class":5036},[1736,12614,10208],{"class":4866},[1736,12616,4914],{"class":1912},[1736,12618,12619,12622,12624,12627],{"class":1738,"line":2353},[1736,12620,12621],{"class":2674},"          setSelectedSize",[1736,12623,7751],{"class":1912},[1736,12625,12626],{"class":2674},"parseInt",[1736,12628,12629],{"class":1912},"(size))\n",[1736,12631,12632],{"class":1738,"line":2358},[1736,12633,10300],{"class":1912},[1736,12635,12636,12639,12641],{"class":1738,"line":2364},[1736,12637,12638],{"class":2674},"        availableSizes",[1736,12640,5062],{"class":4866},[1736,12642,12643],{"class":1912},"{sizes}\n",[1736,12645,12646],{"class":1738,"line":2370},[1736,12647,9195],{"class":1912},[1736,12649,12650,12652,12654,12657,12659],{"class":1738,"line":2376},[1736,12651,6710],{"class":1912},[1736,12653,11],{"class":6696},[1736,12655,12656],{"class":1912},">You're selected size is: {selectedSize}\u003C/",[1736,12658,11],{"class":6696},[1736,12660,6663],{"class":1912},[1736,12662,12663],{"class":1738,"line":2381},[1736,12664,10322],{"class":1912},[1736,12666,12667],{"class":1738,"line":2387},[1736,12668,6753],{"class":1912},[1736,12670,12671],{"class":1738,"line":2393},[1736,12672,1976],{"class":1912},[11,12674,12675],{},"We can now see our component works as we would expect it to. As we are building this component using Bit I have a dev server that shows me the component running in isolation. If you are not using Bit then you will need to import it into another component to see it working.",[731,12677],{},[138,12679,12681],{"id":12680},"writing-tests","Writing Tests",[11,12683,12684],{},"We can therefore move on to write the tests for this component and use the composition created in order to test it.",[138,12686,12688],{"id":12687},"importing-what-we-need","Importing what we need",[11,12690,12691,12692,12695,12696,12698],{},"We are using Testing Library to test our component so we need to import ",[179,12693,12694],{},"render, screen, userEvent"," from ",[179,12697,10350],{}," as well as React from 'react'. We also need to import our composition component as our tests are based on the composition we created earlier.",[299,12700,12702],{"className":4894,"code":12701,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n",[179,12703,12704,12714,12724,12734],{"__ignoreMap":307},[1736,12705,12706,12708,12710,12712],{"class":1738,"line":1739},[1736,12707,4996],{"class":4866},[1736,12709,6594],{"class":1912},[1736,12711,5002],{"class":4866},[1736,12713,6599],{"class":1935},[1736,12715,12716,12718,12720,12722],{"class":1738,"line":748},[1736,12717,4996],{"class":4866},[1736,12719,10384],{"class":1912},[1736,12721,5002],{"class":4866},[1736,12723,10389],{"class":1935},[1736,12725,12726,12728,12730,12732],{"class":1738,"line":756},[1736,12727,4996],{"class":4866},[1736,12729,10396],{"class":1912},[1736,12731,5002],{"class":4866},[1736,12733,10401],{"class":1935},[1736,12735,12736,12738,12741,12743],{"class":1738,"line":1755},[1736,12737,4996],{"class":4866},[1736,12739,12740],{"class":1912}," { SelectSizeAndShowSelectedSize } ",[1736,12742,5002],{"class":4866},[1736,12744,12745],{"class":1935}," './select-size.composition'\n",[138,12747,12749],{"id":12748},"describing-our-test","Describing our Test",[11,12751,12752],{},"Our test should check that the value changes when the user chooses a new size so we can start with that as a description.",[299,12754,12756],{"className":4894,"code":12755,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {})\n",[179,12757,12758,12768,12778,12788,12798,12802],{"__ignoreMap":307},[1736,12759,12760,12762,12764,12766],{"class":1738,"line":1739},[1736,12761,4996],{"class":4866},[1736,12763,6594],{"class":1912},[1736,12765,5002],{"class":4866},[1736,12767,6599],{"class":1935},[1736,12769,12770,12772,12774,12776],{"class":1738,"line":748},[1736,12771,4996],{"class":4866},[1736,12773,10384],{"class":1912},[1736,12775,5002],{"class":4866},[1736,12777,10389],{"class":1935},[1736,12779,12780,12782,12784,12786],{"class":1738,"line":756},[1736,12781,4996],{"class":4866},[1736,12783,10396],{"class":1912},[1736,12785,5002],{"class":4866},[1736,12787,10401],{"class":1935},[1736,12789,12790,12792,12794,12796],{"class":1738,"line":1755},[1736,12791,4996],{"class":4866},[1736,12793,12740],{"class":1912},[1736,12795,5002],{"class":4866},[1736,12797,12745],{"class":1935},[1736,12799,12800],{"class":1738,"line":1761},[1736,12801,1747],{"emptyLinePlaceholder":790},[1736,12803,12804,12806,12808,12811,12813,12815],{"class":1738,"line":1767},[1736,12805,10517],{"class":2674},[1736,12807,7751],{"class":1912},[1736,12809,12810],{"class":1935},"'checks value changes when user chooses a new size'",[1736,12812,10524],{"class":1912},[1736,12814,7013],{"class":4866},[1736,12816,12817],{"class":1912}," {})\n",[138,12819,12821],{"id":12820},"rendering-our-composition-component","Rendering our Composition Component",[11,12823,12824],{},"We then render the component we want to test which is the component we created in our composition file which uses our select size component and also adds in a ",[11,12826,12458,12827,12829],{},[179,12828,12424],{}," so we can see the size of the product selected as we change it.",[299,12831,12833],{"className":4894,"code":12832,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {\n  render(\u003CSelectSizeAndShowSelectedSize />)\n})\n",[179,12834,12835,12845,12855,12865,12875,12879,12893,12903],{"__ignoreMap":307},[1736,12836,12837,12839,12841,12843],{"class":1738,"line":1739},[1736,12838,4996],{"class":4866},[1736,12840,6594],{"class":1912},[1736,12842,5002],{"class":4866},[1736,12844,6599],{"class":1935},[1736,12846,12847,12849,12851,12853],{"class":1738,"line":748},[1736,12848,4996],{"class":4866},[1736,12850,10384],{"class":1912},[1736,12852,5002],{"class":4866},[1736,12854,10389],{"class":1935},[1736,12856,12857,12859,12861,12863],{"class":1738,"line":756},[1736,12858,4996],{"class":4866},[1736,12860,10396],{"class":1912},[1736,12862,5002],{"class":4866},[1736,12864,10401],{"class":1935},[1736,12866,12867,12869,12871,12873],{"class":1738,"line":1755},[1736,12868,4996],{"class":4866},[1736,12870,12740],{"class":1912},[1736,12872,5002],{"class":4866},[1736,12874,12745],{"class":1935},[1736,12876,12877],{"class":1738,"line":1761},[1736,12878,1747],{"emptyLinePlaceholder":790},[1736,12880,12881,12883,12885,12887,12889,12891],{"class":1738,"line":1767},[1736,12882,10517],{"class":2674},[1736,12884,7751],{"class":1912},[1736,12886,12810],{"class":1935},[1736,12888,10524],{"class":1912},[1736,12890,7013],{"class":4866},[1736,12892,4914],{"class":1912},[1736,12894,12895,12897,12899,12901],{"class":1738,"line":1772},[1736,12896,10533],{"class":2674},[1736,12898,10536],{"class":1912},[1736,12900,12409],{"class":1918},[1736,12902,10541],{"class":1912},[1736,12904,12905],{"class":1738,"line":1778},[1736,12906,10582],{"class":1912},[138,12908,12910],{"id":12909},"checking-what-role-exists","Checking what Role Exists",[11,12912,12913,12914,12917],{},"In order to see what role is available can use the ",[179,12915,12916],{},"screen.getByRole"," function and pass in any string. This will tell us that the role we are looking for doesn't exist but will show us what roles do exist on our component.",[299,12919,12921],{"className":4894,"code":12920,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event';\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {\n  render(\u003CSelectSizeAndShowSelectedSize />)\n  const selectSizeAndShowSelectedSize = screen.getByRole('blah')\n",[179,12922,12923,12933,12943,12956,12966,12970,12984,12994],{"__ignoreMap":307},[1736,12924,12925,12927,12929,12931],{"class":1738,"line":1739},[1736,12926,4996],{"class":4866},[1736,12928,6594],{"class":1912},[1736,12930,5002],{"class":4866},[1736,12932,6599],{"class":1935},[1736,12934,12935,12937,12939,12941],{"class":1738,"line":748},[1736,12936,4996],{"class":4866},[1736,12938,10384],{"class":1912},[1736,12940,5002],{"class":4866},[1736,12942,10389],{"class":1935},[1736,12944,12945,12947,12949,12951,12954],{"class":1738,"line":756},[1736,12946,4996],{"class":4866},[1736,12948,10396],{"class":1912},[1736,12950,5002],{"class":4866},[1736,12952,12953],{"class":1935}," '@testing-library/user-event'",[1736,12955,7682],{"class":1912},[1736,12957,12958,12960,12962,12964],{"class":1738,"line":1755},[1736,12959,4996],{"class":4866},[1736,12961,12740],{"class":1912},[1736,12963,5002],{"class":4866},[1736,12965,12745],{"class":1935},[1736,12967,12968],{"class":1738,"line":1761},[1736,12969,1747],{"emptyLinePlaceholder":790},[1736,12971,12972,12974,12976,12978,12980,12982],{"class":1738,"line":1767},[1736,12973,10517],{"class":2674},[1736,12975,7751],{"class":1912},[1736,12977,12810],{"class":1935},[1736,12979,10524],{"class":1912},[1736,12981,7013],{"class":4866},[1736,12983,4914],{"class":1912},[1736,12985,12986,12988,12990,12992],{"class":1738,"line":1772},[1736,12987,10533],{"class":2674},[1736,12989,10536],{"class":1912},[1736,12991,12409],{"class":1918},[1736,12993,10541],{"class":1912},[1736,12995,12996,12998,13001,13003,13005,13007,13009,13012],{"class":1738,"line":1778},[1736,12997,7824],{"class":4866},[1736,12999,13000],{"class":1918}," selectSizeAndShowSelectedSize",[1736,13002,4911],{"class":4866},[1736,13004,10551],{"class":1912},[1736,13006,1032],{"class":2674},[1736,13008,7751],{"class":1912},[1736,13010,13011],{"class":1935},"'blah'",[1736,13013,7045],{"class":1912},[138,13015,13017],{"id":13016},"getting-by-correct-role","Getting by Correct Role",[11,13019,13020,13021,13024,13025,13028,13029,13032],{},"As we are running our tests in watch mode we can see that the role ",[179,13022,13023],{},"blah"," does not exist but it tells us that ",[179,13026,13027],{},"combobox"," does exist meaning we can use this for our role. We can also pass in the name with the value of the label. This also makes sure we have the correct label. Adding in a regex with ",[179,13030,13031],{},"i"," at the end means we won't have to worry about case sensitivity.",[299,13034,13036],{"className":4894,"code":13035,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {\n  render(\u003CSelectSizeAndShowSelectedSize />)\n  const selectSizeAndShowSelectedSize = screen.getByRole('combobox', {\n    name: /choose a size/i\n  })\n})\n",[179,13037,13038,13048,13058,13068,13078,13082,13096,13106,13126,13143,13148],{"__ignoreMap":307},[1736,13039,13040,13042,13044,13046],{"class":1738,"line":1739},[1736,13041,4996],{"class":4866},[1736,13043,6594],{"class":1912},[1736,13045,5002],{"class":4866},[1736,13047,6599],{"class":1935},[1736,13049,13050,13052,13054,13056],{"class":1738,"line":748},[1736,13051,4996],{"class":4866},[1736,13053,10384],{"class":1912},[1736,13055,5002],{"class":4866},[1736,13057,10389],{"class":1935},[1736,13059,13060,13062,13064,13066],{"class":1738,"line":756},[1736,13061,4996],{"class":4866},[1736,13063,10396],{"class":1912},[1736,13065,5002],{"class":4866},[1736,13067,10401],{"class":1935},[1736,13069,13070,13072,13074,13076],{"class":1738,"line":1755},[1736,13071,4996],{"class":4866},[1736,13073,12740],{"class":1912},[1736,13075,5002],{"class":4866},[1736,13077,12745],{"class":1935},[1736,13079,13080],{"class":1738,"line":1761},[1736,13081,1747],{"emptyLinePlaceholder":790},[1736,13083,13084,13086,13088,13090,13092,13094],{"class":1738,"line":1767},[1736,13085,10517],{"class":2674},[1736,13087,7751],{"class":1912},[1736,13089,12810],{"class":1935},[1736,13091,10524],{"class":1912},[1736,13093,7013],{"class":4866},[1736,13095,4914],{"class":1912},[1736,13097,13098,13100,13102,13104],{"class":1738,"line":1772},[1736,13099,10533],{"class":2674},[1736,13101,10536],{"class":1912},[1736,13103,12409],{"class":1918},[1736,13105,10541],{"class":1912},[1736,13107,13108,13110,13112,13114,13116,13118,13120,13123],{"class":1738,"line":1778},[1736,13109,7824],{"class":4866},[1736,13111,13000],{"class":1918},[1736,13113,4911],{"class":4866},[1736,13115,10551],{"class":1912},[1736,13117,1032],{"class":2674},[1736,13119,7751],{"class":1912},[1736,13121,13122],{"class":1935},"'combobox'",[1736,13124,13125],{"class":1912},", {\n",[1736,13127,13128,13131,13134,13138,13140],{"class":1738,"line":1784},[1736,13129,13130],{"class":1912},"    name:",[1736,13132,13133],{"class":1935}," /",[1736,13135,13137],{"class":13136},"sA_wV","choose a size",[1736,13139,1066],{"class":1935},[1736,13141,13142],{"class":4866},"i\n",[1736,13144,13145],{"class":1738,"line":1790},[1736,13146,13147],{"class":1912},"  })\n",[1736,13149,13150],{"class":1738,"line":1796},[1736,13151,10582],{"class":1912},[138,13153,13155],{"id":13154},"expecting-our-component-to-have-correct-value","Expecting our Component to Have Correct Value",[11,13157,13158,13159,13162,13163,13165,13166,891],{},"We now use ",[179,13160,13161],{},"expect"," to make sure our component has the correct value which will be the default value we set it to. We can see what value this is by first adding in any value such as ",[179,13164,1290],{}," and seeing our test fail. The failing test will tell us what value it is expecting which should be the first value in our array that we created in the composition file, ",[179,13167,12517],{},[299,13169,13171],{"className":4894,"code":13170,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {\n  render(\u003CSelectSizeAndShowSelectedSize />)\n  const selectSizeAndShowSelectedSize = screen.getByRole('combobox', {\n    name: /choose a size/i\n  })\n  expect(selectSizeAndShowSelectedSize).toHaveValue('36')\n})\n",[179,13172,13173,13183,13193,13203,13213,13217,13231,13241,13259,13271,13275,13291],{"__ignoreMap":307},[1736,13174,13175,13177,13179,13181],{"class":1738,"line":1739},[1736,13176,4996],{"class":4866},[1736,13178,6594],{"class":1912},[1736,13180,5002],{"class":4866},[1736,13182,6599],{"class":1935},[1736,13184,13185,13187,13189,13191],{"class":1738,"line":748},[1736,13186,4996],{"class":4866},[1736,13188,10384],{"class":1912},[1736,13190,5002],{"class":4866},[1736,13192,10389],{"class":1935},[1736,13194,13195,13197,13199,13201],{"class":1738,"line":756},[1736,13196,4996],{"class":4866},[1736,13198,10396],{"class":1912},[1736,13200,5002],{"class":4866},[1736,13202,10401],{"class":1935},[1736,13204,13205,13207,13209,13211],{"class":1738,"line":1755},[1736,13206,4996],{"class":4866},[1736,13208,12740],{"class":1912},[1736,13210,5002],{"class":4866},[1736,13212,12745],{"class":1935},[1736,13214,13215],{"class":1738,"line":1761},[1736,13216,1747],{"emptyLinePlaceholder":790},[1736,13218,13219,13221,13223,13225,13227,13229],{"class":1738,"line":1767},[1736,13220,10517],{"class":2674},[1736,13222,7751],{"class":1912},[1736,13224,12810],{"class":1935},[1736,13226,10524],{"class":1912},[1736,13228,7013],{"class":4866},[1736,13230,4914],{"class":1912},[1736,13232,13233,13235,13237,13239],{"class":1738,"line":1772},[1736,13234,10533],{"class":2674},[1736,13236,10536],{"class":1912},[1736,13238,12409],{"class":1918},[1736,13240,10541],{"class":1912},[1736,13242,13243,13245,13247,13249,13251,13253,13255,13257],{"class":1738,"line":1778},[1736,13244,7824],{"class":4866},[1736,13246,13000],{"class":1918},[1736,13248,4911],{"class":4866},[1736,13250,10551],{"class":1912},[1736,13252,1032],{"class":2674},[1736,13254,7751],{"class":1912},[1736,13256,13122],{"class":1935},[1736,13258,13125],{"class":1912},[1736,13260,13261,13263,13265,13267,13269],{"class":1738,"line":1784},[1736,13262,13130],{"class":1912},[1736,13264,13133],{"class":1935},[1736,13266,13137],{"class":13136},[1736,13268,1066],{"class":1935},[1736,13270,13142],{"class":4866},[1736,13272,13273],{"class":1738,"line":1790},[1736,13274,13147],{"class":1912},[1736,13276,13277,13279,13282,13284,13286,13289],{"class":1738,"line":1796},[1736,13278,10565],{"class":2674},[1736,13280,13281],{"class":1912},"(selectSizeAndShowSelectedSize).",[1736,13283,10571],{"class":2674},[1736,13285,7751],{"class":1912},[1736,13287,13288],{"class":1935},"'36'",[1736,13290,7045],{"class":1912},[1736,13292,13293],{"class":1738,"line":2353},[1736,13294,10582],{"class":1912},[138,13296,13298],{"id":13297},"firing-an-event-and-expecting-the-value-to-change","Firing an Event and Expecting the Value to Change",[11,13300,13301,13302,13304,13305,13308,13309,13312,13313,13316,13317,13319,13320,891],{},"As we want to make sure the value is updated when the user chooses a new size we can use the ",[179,13303,10358],{}," method with the ",[179,13306,13307],{},"change"," function passing in what we want to change and what the target is. In our case it is the const of ",[179,13310,13311],{},"selectSizeAndShowSelectedSize"," and the target is the ",[179,13314,13315],{},"value"," and we can add in what value we want to change it to. We then use the ",[179,13318,13161],{}," method to make sure the value has been updated correctly to the new value of the ",[179,13321,10358],{},[299,13323,13325],{"className":4894,"code":13324,"language":4896,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport { SelectSizeAndShowSelectedSize } from './select-size.composition'\n\nit('checks value changes when user chooses a new size', () => {\n  render(\u003CSelectSizeAndShowSelectedSize />)\n  const selectSizeAndShowSelectedSize = screen.getByRole('combobox', {\n    name: /choose a size/i\n  })\n  expect(selectSizeAndShowSelectedSize).toHaveValue('36')\n  userEvent.selectOptions(selectSizeAndShowSelectedSize, '45')\n  expect(selectSizeAndShowSelectedSize).toHaveValue('45')\n})\n",[179,13326,13327,13337,13347,13357,13367,13371,13385,13395,13413,13425,13429,13443,13458,13472],{"__ignoreMap":307},[1736,13328,13329,13331,13333,13335],{"class":1738,"line":1739},[1736,13330,4996],{"class":4866},[1736,13332,6594],{"class":1912},[1736,13334,5002],{"class":4866},[1736,13336,6599],{"class":1935},[1736,13338,13339,13341,13343,13345],{"class":1738,"line":748},[1736,13340,4996],{"class":4866},[1736,13342,10384],{"class":1912},[1736,13344,5002],{"class":4866},[1736,13346,10389],{"class":1935},[1736,13348,13349,13351,13353,13355],{"class":1738,"line":756},[1736,13350,4996],{"class":4866},[1736,13352,10396],{"class":1912},[1736,13354,5002],{"class":4866},[1736,13356,10401],{"class":1935},[1736,13358,13359,13361,13363,13365],{"class":1738,"line":1755},[1736,13360,4996],{"class":4866},[1736,13362,12740],{"class":1912},[1736,13364,5002],{"class":4866},[1736,13366,12745],{"class":1935},[1736,13368,13369],{"class":1738,"line":1761},[1736,13370,1747],{"emptyLinePlaceholder":790},[1736,13372,13373,13375,13377,13379,13381,13383],{"class":1738,"line":1767},[1736,13374,10517],{"class":2674},[1736,13376,7751],{"class":1912},[1736,13378,12810],{"class":1935},[1736,13380,10524],{"class":1912},[1736,13382,7013],{"class":4866},[1736,13384,4914],{"class":1912},[1736,13386,13387,13389,13391,13393],{"class":1738,"line":1772},[1736,13388,10533],{"class":2674},[1736,13390,10536],{"class":1912},[1736,13392,12409],{"class":1918},[1736,13394,10541],{"class":1912},[1736,13396,13397,13399,13401,13403,13405,13407,13409,13411],{"class":1738,"line":1778},[1736,13398,7824],{"class":4866},[1736,13400,13000],{"class":1918},[1736,13402,4911],{"class":4866},[1736,13404,10551],{"class":1912},[1736,13406,1032],{"class":2674},[1736,13408,7751],{"class":1912},[1736,13410,13122],{"class":1935},[1736,13412,13125],{"class":1912},[1736,13414,13415,13417,13419,13421,13423],{"class":1738,"line":1784},[1736,13416,13130],{"class":1912},[1736,13418,13133],{"class":1935},[1736,13420,13137],{"class":13136},[1736,13422,1066],{"class":1935},[1736,13424,13142],{"class":4866},[1736,13426,13427],{"class":1738,"line":1790},[1736,13428,13147],{"class":1912},[1736,13430,13431,13433,13435,13437,13439,13441],{"class":1738,"line":1796},[1736,13432,10565],{"class":2674},[1736,13434,13281],{"class":1912},[1736,13436,10571],{"class":2674},[1736,13438,7751],{"class":1912},[1736,13440,13288],{"class":1935},[1736,13442,7045],{"class":1912},[1736,13444,13445,13447,13450,13453,13456],{"class":1738,"line":2353},[1736,13446,10801],{"class":1912},[1736,13448,13449],{"class":2674},"selectOptions",[1736,13451,13452],{"class":1912},"(selectSizeAndShowSelectedSize, ",[1736,13454,13455],{"class":1935},"'45'",[1736,13457,7045],{"class":1912},[1736,13459,13460,13462,13464,13466,13468,13470],{"class":1738,"line":2358},[1736,13461,10565],{"class":2674},[1736,13463,13281],{"class":1912},[1736,13465,10571],{"class":2674},[1736,13467,7751],{"class":1912},[1736,13469,13455],{"class":1935},[1736,13471,7045],{"class":1912},[1736,13473,13474],{"class":1738,"line":2364},[1736,13475,10582],{"class":1912},[731,13477],{},[23,13479,3294],{"id":3293},[11,13481,13482],{},"And that's it. We now have a select component that works as we would expect and can now be used in the component where it should be used knowing that it will work correctly. Compositions are a great way of seeing the different states of our components and we can then use the composition file to understand what we need to do to make our component work when using it in our next component/app.",[11,13484,13485],{},[121,13486],{"alt":13487,"src":13488},"select element changing size on click","https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1641812321/debbie.codes/blog/2022/select-on-change_bvkj1x.gif",[11,13490,11355],{},[23,13492,13494],{"id":13493},"using-the-component","Using the Component",[11,13496,2506,13497,13501],{},[15,13498,13500],{"href":11446,"rel":13499},[19],"select size component can be found here"," and is fully open source meaning you can install it in your own project using bit, npm or yarn so feel free to take it for a test drive.",[299,13503,13505],{"className":2665,"code":13504,"language":2667,"meta":307,"style":307},"bit install @learn-bit-react/ecommerce.ui.product.select-size\n\nnpm i @learn-bit-react/ecommerce.ui.product.select-size\n\nyarn add @learn-bit-react/ecommerce.ui.product.select-size\n",[179,13506,13507,13516,13520,13528,13532],{"__ignoreMap":307},[1736,13508,13509,13511,13513],{"class":1738,"line":1739},[1736,13510,4970],{"class":2674},[1736,13512,4973],{"class":1935},[1736,13514,13515],{"class":1935}," @learn-bit-react/ecommerce.ui.product.select-size\n",[1736,13517,13518],{"class":1738,"line":748},[1736,13519,1747],{"emptyLinePlaceholder":790},[1736,13521,13522,13524,13526],{"class":1738,"line":756},[1736,13523,6565],{"class":2674},[1736,13525,6568],{"class":1935},[1736,13527,13515],{"class":1935},[1736,13529,13530],{"class":1738,"line":1755},[1736,13531,1747],{"emptyLinePlaceholder":790},[1736,13533,13534,13536,13538],{"class":1738,"line":1761},[1736,13535,6575],{"class":2674},[1736,13537,2681],{"class":1935},[1736,13539,13515],{"class":1935},[23,13541,5706],{"id":5705},[70,13543,13544,13550,13557,13564,13571,13578],{},[73,13545,13546],{},[15,13547,13549],{"href":11446,"rel":13548},[19],"Select Size Component",[73,13551,13552],{},[15,13553,13556],{"href":13554,"rel":13555},"https://bit.dev/learn-bit-react/ecommerce/ui/product/select-size/~code/select-size.tsx",[19],"Select Size Component Code",[73,13558,13559],{},[15,13560,13563],{"href":13561,"rel":13562},"https://bit.dev/learn-bit-react/ecommerce/ui/product/select-size/~code/select-size.composition.tsx",[19],"Select Size Component composition",[73,13565,13566],{},[15,13567,13570],{"href":13568,"rel":13569},"https://bit.dev/learn-bit-react/ecommerce/ui/product/select-size/~code/select-size.spec.tsx",[19],"Select Size Test File",[73,13572,13573],{},[15,13574,13577],{"href":13575,"rel":13576},"https://testing-library.com/docs/ecosystem-user-event/#selectoptionselement-values-options",[19],"Testing Library Docs",[73,13579,13580],{},[15,13581,13584],{"href":13582,"rel":13583},"https://beta.reactjs.org/reference/usestate",[19],"docs for useState()",[2011,13586,13587],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":307,"searchDepth":748,"depth":748,"links":13589},[13590,13599,13609,13610,13611],{"id":11459,"depth":748,"text":11460,"children":13591},[13592,13593,13594,13595,13596,13597,13598],{"id":11468,"depth":756,"text":11469},{"id":11525,"depth":756,"text":11526},{"id":11634,"depth":756,"text":11635},{"id":11691,"depth":756,"text":11692},{"id":11784,"depth":756,"text":11785},{"id":11988,"depth":756,"text":11989},{"id":12041,"depth":756,"text":12042},{"id":12399,"depth":748,"text":12400,"children":13600},[13601,13602,13603,13604,13605,13606,13607,13608],{"id":12680,"depth":756,"text":12681},{"id":12687,"depth":756,"text":12688},{"id":12748,"depth":756,"text":12749},{"id":12820,"depth":756,"text":12821},{"id":12909,"depth":756,"text":12910},{"id":13016,"depth":756,"text":13017},{"id":13154,"depth":756,"text":13155},{"id":13297,"depth":756,"text":13298},{"id":3293,"depth":748,"text":3294},{"id":13493,"depth":748,"text":13494},{"id":5705,"depth":748,"text":5706},"2022-01-10","Building a Select component with a Composition to see the component render in isolation as well as Tests using React Testing Library to test the select works on change and shows a new value.","v1641812664/debbie.codes/blog/2022/select_2x_g2mbjj",{},"/blog/building-and-testing-select-input",{"title":11426,"description":13613},"blog/building-and-testing-select-input",[5221,1411],"OWQe7QqyolFDZIE8ka3b2gp3z3goIW_IVzDNZh9lyV8",{"id":13622,"title":13623,"body":13624,"canonical":788,"date":15410,"description":15411,"extension":786,"featured":787,"image":15412,"meta":15413,"navigation":790,"ogimage":788,"path":15414,"provider":3460,"published":787,"seo":15415,"stem":15416,"tags":15417,"url":788,"__hash__":15418},"blog/blog/building-cart-context-component.md","Building a Cart Context Component",{"type":8,"value":13625,"toc":15401},[13626,13635,13638,13644,13647,13651,13659,13662,13692,13695,13771,13774,13906,13909,13969,13972,14021,14025,14034,14102,14105,14108,14111,14611,14615,14618,14627,14650,14653,14656,14887,14890,14893,14896,15093,15096,15180,15183,15187,15190,15204,15207,15216,15220,15349,15351,15398],[11,13627,13628,13629,13634],{},"When I was first asked to create a ",[15,13630,13633],{"href":13631,"rel":13632},"https://bit-shoe-store.netlify.app/product/h-3",[19],"shopping cart component"," I found it really difficult to get my head round it especially when it came to breaking it down into smaller reusable components. I won't lie to you. I actually built it as one big component and then started separating it into smaller pieces. I think sometimes that is also ok to do. It's not always easy to build things individually or at least it does take a bit of practice.",[11,13636,13637],{},"In this article I will go through the process of building a cart context component as a generic component that can then be used by any other component so as to create a shoe cart component for a shoe store or a different type of cart component for a different type of store. In a future post I will show you how I then used this context to do that but for now let's concentrate on getting the generic context created.",[11,13639,13640],{},[121,13641],{"alt":13642,"src":13643},"Cart Context Component","https://res.cloudinary.com/debsobrien/image/upload/v1643820029/debbie.codes/blog/2022/cart-context_2x_qlqg5q.png",[11,13645,13646],{},"To start off with we need to think about what we are trying to create. We want to have a context component that is a generic cart context using generic products with functionality to add and remove a product from the cart. Sounds simple enough and that is exactly what we should build.",[23,13648,13650],{"id":13649},"creating-the-context","Creating the Context",[11,13652,13653,13654,891],{},"If we were to split these components by team ownership it would be the e-commerce team who would own and be responsible for building it. The first component we will build is the cart ",[15,13655,13658],{"href":13656,"rel":13657},"https://bit.dev/learn-bit-react/ecommerce/ui/cart/cart-context/~code/cart-context.tsx",[19],"context component",[11,13660,13661],{},"To build our context we start off with creating our types. We want to have an interface for the Cart base Item which will contain the id",[299,13663,13665],{"className":8734,"code":13664,"language":8736,"meta":307,"style":307},"export interface CartItemBase {\n  id: string\n}\n",[179,13666,13667,13679,13688],{"__ignoreMap":307},[1736,13668,13669,13671,13674,13677],{"class":1738,"line":1739},[1736,13670,6632],{"class":4866},[1736,13672,13673],{"class":4866}," interface",[1736,13675,13676],{"class":2674}," CartItemBase",[1736,13678,4914],{"class":1912},[1736,13680,13681,13684,13686],{"class":1738,"line":748},[1736,13682,13683],{"class":5036},"  id",[1736,13685,1087],{"class":4866},[1736,13687,6866],{"class":1918},[1736,13689,13690],{"class":1738,"line":756},[1736,13691,1976],{"class":1912},[11,13693,13694],{},"The cart list item will extend the cart base item and will show the items in the cart as well as the quantity of items",[299,13696,13698],{"className":8734,"code":13697,"language":8736,"meta":307,"style":307},"export interface CartListItem\u003CTItemType extends CartItemBase> {\n  /**\n   * item in cart\n   */\n  item: TItemType\n  /**\n   * quantity of item in cart\n   */\n  quantity: number\n}\n",[179,13699,13700,13722,13726,13731,13735,13745,13749,13754,13758,13767],{"__ignoreMap":307},[1736,13701,13702,13704,13706,13709,13711,13714,13717,13719],{"class":1738,"line":1739},[1736,13703,6632],{"class":4866},[1736,13705,13673],{"class":4866},[1736,13707,13708],{"class":2674}," CartListItem",[1736,13710,6657],{"class":1912},[1736,13712,13713],{"class":2674},"TItemType",[1736,13715,13716],{"class":4866}," extends",[1736,13718,13676],{"class":2674},[1736,13720,13721],{"class":1912},"> {\n",[1736,13723,13724],{"class":1738,"line":748},[1736,13725,6821],{"class":6820},[1736,13727,13728],{"class":1738,"line":756},[1736,13729,13730],{"class":6820},"   * item in cart\n",[1736,13732,13733],{"class":1738,"line":1755},[1736,13734,6831],{"class":6820},[1736,13736,13737,13740,13742],{"class":1738,"line":1761},[1736,13738,13739],{"class":5036},"  item",[1736,13741,1087],{"class":4866},[1736,13743,13744],{"class":2674}," TItemType\n",[1736,13746,13747],{"class":1738,"line":1767},[1736,13748,6821],{"class":6820},[1736,13750,13751],{"class":1738,"line":1772},[1736,13752,13753],{"class":6820},"   * quantity of item in cart\n",[1736,13755,13756],{"class":1738,"line":1778},[1736,13757,6831],{"class":6820},[1736,13759,13760,13763,13765],{"class":1738,"line":1784},[1736,13761,13762],{"class":5036},"  quantity",[1736,13764,1087],{"class":4866},[1736,13766,9584],{"class":1918},[1736,13768,13769],{"class":1738,"line":1790},[1736,13770,1976],{"class":1912},[11,13772,13773],{},"And finally we create the cart Context Type which also extends the cart base item. This gives us the cart as well as the function to add products and the function to remove products. Every cart should have these types.",[299,13775,13777],{"className":8734,"code":13776,"language":8736,"meta":307,"style":307},"export interface CartContextType\u003CTItemType extends CartItemBase> {\n  /**\n   * items in cart\n   */\n  cart: CartListItem\u003CTItemType>[]\n  /**\n   * adds products to cart\n   */\n  addProductToCart: (item: CartListItem\u003CTItemType>) => void\n  /**\n   * removes products from cart\n   */\n  removeProductFromCart: (item: TItemType) => void\n}\n",[179,13778,13779,13798,13802,13807,13811,13827,13831,13836,13840,13867,13871,13876,13880,13902],{"__ignoreMap":307},[1736,13780,13781,13783,13785,13788,13790,13792,13794,13796],{"class":1738,"line":1739},[1736,13782,6632],{"class":4866},[1736,13784,13673],{"class":4866},[1736,13786,13787],{"class":2674}," CartContextType",[1736,13789,6657],{"class":1912},[1736,13791,13713],{"class":2674},[1736,13793,13716],{"class":4866},[1736,13795,13676],{"class":2674},[1736,13797,13721],{"class":1912},[1736,13799,13800],{"class":1738,"line":748},[1736,13801,6821],{"class":6820},[1736,13803,13804],{"class":1738,"line":756},[1736,13805,13806],{"class":6820},"   * items in cart\n",[1736,13808,13809],{"class":1738,"line":1755},[1736,13810,6831],{"class":6820},[1736,13812,13813,13816,13818,13820,13822,13824],{"class":1738,"line":1761},[1736,13814,13815],{"class":5036},"  cart",[1736,13817,1087],{"class":4866},[1736,13819,13708],{"class":2674},[1736,13821,6657],{"class":1912},[1736,13823,13713],{"class":2674},[1736,13825,13826],{"class":1912},">[]\n",[1736,13828,13829],{"class":1738,"line":1767},[1736,13830,6821],{"class":6820},[1736,13832,13833],{"class":1738,"line":1772},[1736,13834,13835],{"class":6820},"   * adds products to cart\n",[1736,13837,13838],{"class":1738,"line":1778},[1736,13839,6831],{"class":6820},[1736,13841,13842,13845,13847,13849,13852,13854,13856,13858,13860,13863,13865],{"class":1738,"line":1784},[1736,13843,13844],{"class":2674},"  addProductToCart",[1736,13846,1087],{"class":4866},[1736,13848,1095],{"class":1912},[1736,13850,13851],{"class":5036},"item",[1736,13853,1087],{"class":4866},[1736,13855,13708],{"class":2674},[1736,13857,6657],{"class":1912},[1736,13859,13713],{"class":2674},[1736,13861,13862],{"class":1912},">) ",[1736,13864,7013],{"class":4866},[1736,13866,8944],{"class":1918},[1736,13868,13869],{"class":1738,"line":1790},[1736,13870,6821],{"class":6820},[1736,13872,13873],{"class":1738,"line":1796},[1736,13874,13875],{"class":6820},"   * removes products from cart\n",[1736,13877,13878],{"class":1738,"line":2353},[1736,13879,6831],{"class":6820},[1736,13881,13882,13885,13887,13889,13891,13893,13896,13898,13900],{"class":1738,"line":2358},[1736,13883,13884],{"class":2674},"  removeProductFromCart",[1736,13886,1087],{"class":4866},[1736,13888,1095],{"class":1912},[1736,13890,13851],{"class":5036},[1736,13892,1087],{"class":4866},[1736,13894,13895],{"class":2674}," TItemType",[1736,13897,8939],{"class":1912},[1736,13899,7013],{"class":4866},[1736,13901,8944],{"class":1918},[1736,13903,13904],{"class":1738,"line":2364},[1736,13905,1976],{"class":1912},[11,13907,13908],{},"We can now go ahead and create a default context for our cart to show these cart items and add and remove functions.",[299,13910,13912],{"className":8734,"code":13911,"language":8736,"meta":307,"style":307},"const defaultContext: CartContextType\u003Cany> = {\n  cart: [],\n  addProductToCart: () => {},\n  removeProductFromCart: () => {}\n}\n",[179,13913,13914,13937,13942,13954,13965],{"__ignoreMap":307},[1736,13915,13916,13918,13921,13923,13925,13927,13930,13933,13935],{"class":1738,"line":1739},[1736,13917,5029],{"class":4866},[1736,13919,13920],{"class":1918}," defaultContext",[1736,13922,1087],{"class":4866},[1736,13924,13787],{"class":2674},[1736,13926,6657],{"class":1912},[1736,13928,13929],{"class":1918},"any",[1736,13931,13932],{"class":1912},"> ",[1736,13934,5062],{"class":4866},[1736,13936,4914],{"class":1912},[1736,13938,13939],{"class":1738,"line":748},[1736,13940,13941],{"class":1912},"  cart: [],\n",[1736,13943,13944,13946,13949,13951],{"class":1738,"line":756},[1736,13945,13844],{"class":2674},[1736,13947,13948],{"class":1912},": () ",[1736,13950,7013],{"class":4866},[1736,13952,13953],{"class":1912}," {},\n",[1736,13955,13956,13958,13960,13962],{"class":1738,"line":1755},[1736,13957,13884],{"class":2674},[1736,13959,13948],{"class":1912},[1736,13961,7013],{"class":4866},[1736,13963,13964],{"class":1912}," {}\n",[1736,13966,13967],{"class":1738,"line":1761},[1736,13968,1976],{"class":1912},[11,13970,13971],{},"And finally we create our CreateCartContext function that will create our context and pass in the default context.",[299,13973,13975],{"className":8734,"code":13974,"language":8736,"meta":307,"style":307},"export function CreateCartContext\u003CTItemType extends CartItemBase>() {\n  return createContext \u003C CartContextType \u003C TItemType >> (defaultContext)\n}\n",[179,13976,13977,13997,14017],{"__ignoreMap":307},[1736,13978,13979,13981,13983,13986,13988,13990,13992,13994],{"class":1738,"line":1739},[1736,13980,6632],{"class":4866},[1736,13982,6674],{"class":4866},[1736,13984,13985],{"class":2674}," CreateCartContext",[1736,13987,6657],{"class":1912},[1736,13989,13713],{"class":2674},[1736,13991,13716],{"class":4866},[1736,13993,13676],{"class":2674},[1736,13995,13996],{"class":1912},">() {\n",[1736,13998,13999,14001,14004,14007,14010,14012,14014],{"class":1738,"line":748},[1736,14000,6685],{"class":4866},[1736,14002,14003],{"class":2674}," createContext",[1736,14005,14006],{"class":1912}," \u003C ",[1736,14008,14009],{"class":2674},"CartContextType",[1736,14011,14006],{"class":1912},[1736,14013,13713],{"class":2674},[1736,14015,14016],{"class":1912}," >> (defaultContext)\n",[1736,14018,14019],{"class":1738,"line":756},[1736,14020,1976],{"class":1912},[23,14022,14024],{"id":14023},"provider-component","Provider Component",[11,14026,14027,14028,14033],{},"We now need to create our ",[15,14029,14032],{"href":14030,"rel":14031},"https://bit.dev/learn-bit-react/ecommerce/ui/cart/cart-context/~code/cart-context-provider.tsx",[19],"provider component",". We start by creating our types for the Cart Context.",[299,14035,14037],{"className":4894,"code":14036,"language":4896,"meta":307,"style":307},"export type CartContextProps\u003CTItemType extends CartItemBase> = {\n  context: React.Context\u003CCartContextType\u003CTItemType>>\n} & HTMLAttributes\u003CHTMLDivElement>\n",[179,14038,14039,14062,14087],{"__ignoreMap":307},[1736,14040,14041,14043,14045,14048,14050,14052,14054,14056,14058,14060],{"class":1738,"line":1739},[1736,14042,6632],{"class":4866},[1736,14044,6635],{"class":4866},[1736,14046,14047],{"class":2674}," CartContextProps",[1736,14049,6657],{"class":1912},[1736,14051,13713],{"class":2674},[1736,14053,13716],{"class":4866},[1736,14055,13676],{"class":2674},[1736,14057,13932],{"class":1912},[1736,14059,5062],{"class":4866},[1736,14061,4914],{"class":1912},[1736,14063,14064,14067,14069,14071,14073,14076,14078,14080,14082,14084],{"class":1738,"line":748},[1736,14065,14066],{"class":5036},"  context",[1736,14068,1087],{"class":4866},[1736,14070,6649],{"class":2674},[1736,14072,891],{"class":1912},[1736,14074,14075],{"class":2674},"Context",[1736,14077,6657],{"class":1912},[1736,14079,14009],{"class":2674},[1736,14081,6657],{"class":1912},[1736,14083,13713],{"class":2674},[1736,14085,14086],{"class":1912},">>\n",[1736,14088,14089,14091,14093,14096,14098,14100],{"class":1738,"line":756},[1736,14090,6871],{"class":1912},[1736,14092,6646],{"class":4866},[1736,14094,14095],{"class":2674}," HTMLAttributes",[1736,14097,6657],{"class":1912},[1736,14099,6660],{"class":2674},[1736,14101,6663],{"class":1912},[11,14103,14104],{},"The provider component passes in children and context and uses useState to manage the state of the products or to know if there are any products in the store. Our function getProductsById checks to see if a product already exits in the cart. In this case we would want to update the quantity and not repeat the product.",[11,14106,14107],{},"Our addProductToCart function will use the getProductById function to see if the product already exists in the cart. If it does then we will update the quantity. If it doesn't then we will add the product to the cart.",[11,14109,14110],{},"The removeProductFromCart function filters the products by id and removes the product from the cart.",[299,14112,14114],{"className":4894,"code":14113,"language":4896,"meta":307,"style":307},"export function CartContextProvider\u003CTItemType extends CartItemBase>({\n  children,\n  context\n}: CartContextProps\u003CTItemType>) {\n  const [products, setProducts] = useState \u003C CartListItem \u003C TItemType > [] > ([])\n\n  const getProductById = (id: string): CartListItem\u003CTItemType> | undefined => {\n    return products.find(p => p.item.id === id)\n  }\n\n  const addProductToCart = (product: CartListItem\u003CTItemType>): void => {\n    {\n      const existingProduct = getProductById(product.item.id)\n      let newState: CartListItem\u003CTItemType>[] = []\n      if (existingProduct) {\n        newState = products.map((p) => {\n          if (p.item.id === existingProduct.item.id) {\n            return {\n              item: p.item,\n              quantity: p.quantity + product.quantity\n            }\n          }\n          return p\n        })\n        setProducts(newState)\n      }\n      setProducts([...products, product])\n    }\n  }\n  const removeProductFromCart = (product: TItemType) => {\n    const newProducts = products.filter(p => p.item.id !== product.id)\n\n    setProducts(newProducts)\n  }\n\n  const contextValue: CartContextType\u003CTItemType> = {\n    cart: products,\n    addProductToCart,\n    removeProductFromCart\n  }\n\n  return \u003Ccontext.Provider value={contextValue}>{children}\u003C/context.Provider>\n}\n",[179,14115,14116,14136,14143,14148,14163,14195,14199,14238,14262,14266,14270,14303,14308,14323,14347,14355,14378,14391,14398,14403,14413,14418,14423,14431,14436,14444,14449,14462,14466,14470,14493,14522,14526,14534,14538,14542,14563,14568,14573,14578,14582,14586,14607],{"__ignoreMap":307},[1736,14117,14118,14120,14122,14125,14127,14129,14131,14133],{"class":1738,"line":1739},[1736,14119,6632],{"class":4866},[1736,14121,6674],{"class":4866},[1736,14123,14124],{"class":2674}," CartContextProvider",[1736,14126,6657],{"class":1912},[1736,14128,13713],{"class":2674},[1736,14130,13716],{"class":4866},[1736,14132,13676],{"class":2674},[1736,14134,14135],{"class":1912},">({\n",[1736,14137,14138,14141],{"class":1738,"line":748},[1736,14139,14140],{"class":5036},"  children",[1736,14142,1939],{"class":1912},[1736,14144,14145],{"class":1738,"line":756},[1736,14146,14147],{"class":5036},"  context\n",[1736,14149,14150,14152,14154,14156,14158,14160],{"class":1738,"line":1755},[1736,14151,9053],{"class":1912},[1736,14153,1087],{"class":4866},[1736,14155,14047],{"class":2674},[1736,14157,6657],{"class":1912},[1736,14159,13713],{"class":2674},[1736,14161,14162],{"class":1912},">) {\n",[1736,14164,14165,14167,14169,14172,14174,14177,14179,14181,14183,14185,14188,14190,14192],{"class":1738,"line":1761},[1736,14166,7824],{"class":4866},[1736,14168,8409],{"class":1912},[1736,14170,14171],{"class":1918},"products",[1736,14173,829],{"class":1912},[1736,14175,14176],{"class":1918},"setProducts",[1736,14178,9277],{"class":1912},[1736,14180,5062],{"class":4866},[1736,14182,9282],{"class":2674},[1736,14184,14006],{"class":1912},[1736,14186,14187],{"class":2674},"CartListItem",[1736,14189,14006],{"class":1912},[1736,14191,13713],{"class":2674},[1736,14193,14194],{"class":1912}," > [] > ([])\n",[1736,14196,14197],{"class":1738,"line":1767},[1736,14198,1747],{"emptyLinePlaceholder":790},[1736,14200,14201,14203,14206,14208,14210,14212,14214,14216,14218,14220,14222,14224,14226,14228,14231,14234,14236],{"class":1738,"line":1772},[1736,14202,7824],{"class":4866},[1736,14204,14205],{"class":2674}," getProductById",[1736,14207,4911],{"class":4866},[1736,14209,1095],{"class":1912},[1736,14211,7167],{"class":5036},[1736,14213,1087],{"class":4866},[1736,14215,6841],{"class":1918},[1736,14217,952],{"class":1912},[1736,14219,1087],{"class":4866},[1736,14221,13708],{"class":2674},[1736,14223,6657],{"class":1912},[1736,14225,13713],{"class":2674},[1736,14227,13932],{"class":1912},[1736,14229,14230],{"class":4866},"|",[1736,14232,14233],{"class":1918}," undefined",[1736,14235,10208],{"class":4866},[1736,14237,4914],{"class":1912},[1736,14239,14240,14243,14246,14248,14250,14252,14254,14257,14259],{"class":1738,"line":1778},[1736,14241,14242],{"class":4866},"    return",[1736,14244,14245],{"class":1912}," products.",[1736,14247,7770],{"class":2674},[1736,14249,7751],{"class":1912},[1736,14251,11],{"class":5036},[1736,14253,10208],{"class":4866},[1736,14255,14256],{"class":1912}," p.item.id ",[1736,14258,7786],{"class":4866},[1736,14260,14261],{"class":1912}," id)\n",[1736,14263,14264],{"class":1738,"line":1784},[1736,14265,1971],{"class":1912},[1736,14267,14268],{"class":1738,"line":1790},[1736,14269,1747],{"emptyLinePlaceholder":790},[1736,14271,14272,14274,14277,14279,14281,14283,14285,14287,14289,14291,14294,14296,14299,14301],{"class":1738,"line":1796},[1736,14273,7824],{"class":4866},[1736,14275,14276],{"class":2674}," addProductToCart",[1736,14278,4911],{"class":4866},[1736,14280,1095],{"class":1912},[1736,14282,7236],{"class":5036},[1736,14284,1087],{"class":4866},[1736,14286,13708],{"class":2674},[1736,14288,6657],{"class":1912},[1736,14290,13713],{"class":2674},[1736,14292,14293],{"class":1912},">)",[1736,14295,1087],{"class":4866},[1736,14297,14298],{"class":1918}," void",[1736,14300,10208],{"class":4866},[1736,14302,4914],{"class":1912},[1736,14304,14305],{"class":1738,"line":2353},[1736,14306,14307],{"class":1912},"    {\n",[1736,14309,14310,14313,14316,14318,14320],{"class":1738,"line":2358},[1736,14311,14312],{"class":4866},"      const",[1736,14314,14315],{"class":1918}," existingProduct",[1736,14317,4911],{"class":4866},[1736,14319,14205],{"class":2674},[1736,14321,14322],{"class":1912},"(product.item.id)\n",[1736,14324,14325,14328,14331,14333,14335,14337,14339,14342,14344],{"class":1738,"line":2364},[1736,14326,14327],{"class":4866},"      let",[1736,14329,14330],{"class":1912}," newState",[1736,14332,1087],{"class":4866},[1736,14334,13708],{"class":2674},[1736,14336,6657],{"class":1912},[1736,14338,13713],{"class":2674},[1736,14340,14341],{"class":1912},">[] ",[1736,14343,5062],{"class":4866},[1736,14345,14346],{"class":1912}," []\n",[1736,14348,14349,14352],{"class":1738,"line":2370},[1736,14350,14351],{"class":4866},"      if",[1736,14353,14354],{"class":1912}," (existingProduct) {\n",[1736,14356,14357,14360,14362,14364,14367,14370,14372,14374,14376],{"class":1738,"line":2376},[1736,14358,14359],{"class":1912},"        newState ",[1736,14361,5062],{"class":4866},[1736,14363,14245],{"class":1912},[1736,14365,14366],{"class":2674},"map",[1736,14368,14369],{"class":1912},"((",[1736,14371,11],{"class":5036},[1736,14373,8939],{"class":1912},[1736,14375,7013],{"class":4866},[1736,14377,4914],{"class":1912},[1736,14379,14380,14383,14386,14388],{"class":1738,"line":2381},[1736,14381,14382],{"class":4866},"          if",[1736,14384,14385],{"class":1912}," (p.item.id ",[1736,14387,7786],{"class":4866},[1736,14389,14390],{"class":1912}," existingProduct.item.id) {\n",[1736,14392,14393,14396],{"class":1738,"line":2387},[1736,14394,14395],{"class":4866},"            return",[1736,14397,4914],{"class":1912},[1736,14399,14400],{"class":1738,"line":2393},[1736,14401,14402],{"class":1912},"              item: p.item,\n",[1736,14404,14405,14408,14410],{"class":1738,"line":2398},[1736,14406,14407],{"class":1912},"              quantity: p.quantity ",[1736,14409,9343],{"class":4866},[1736,14411,14412],{"class":1912}," product.quantity\n",[1736,14414,14415],{"class":1738,"line":2404},[1736,14416,14417],{"class":1912},"            }\n",[1736,14419,14420],{"class":1738,"line":6959},[1736,14421,14422],{"class":1912},"          }\n",[1736,14424,14425,14428],{"class":1738,"line":7296},[1736,14426,14427],{"class":4866},"          return",[1736,14429,14430],{"class":1912}," p\n",[1736,14432,14433],{"class":1738,"line":7305},[1736,14434,14435],{"class":1912},"        })\n",[1736,14437,14438,14441],{"class":1738,"line":7310},[1736,14439,14440],{"class":2674},"        setProducts",[1736,14442,14443],{"class":1912},"(newState)\n",[1736,14445,14446],{"class":1738,"line":9659},[1736,14447,14448],{"class":1912},"      }\n",[1736,14450,14451,14454,14457,14459],{"class":1738,"line":9680},[1736,14452,14453],{"class":2674},"      setProducts",[1736,14455,14456],{"class":1912},"([",[1736,14458,11965],{"class":4866},[1736,14460,14461],{"class":1912},"products, product])\n",[1736,14463,14464],{"class":1738,"line":9699},[1736,14465,9853],{"class":1912},[1736,14467,14468],{"class":1738,"line":9704},[1736,14469,1971],{"class":1912},[1736,14471,14472,14474,14477,14479,14481,14483,14485,14487,14489,14491],{"class":1738,"line":9715},[1736,14473,7824],{"class":4866},[1736,14475,14476],{"class":2674}," removeProductFromCart",[1736,14478,4911],{"class":4866},[1736,14480,1095],{"class":1912},[1736,14482,7236],{"class":5036},[1736,14484,1087],{"class":4866},[1736,14486,13895],{"class":2674},[1736,14488,8939],{"class":1912},[1736,14490,7013],{"class":4866},[1736,14492,4914],{"class":1912},[1736,14494,14495,14498,14501,14503,14505,14508,14510,14512,14514,14516,14519],{"class":1738,"line":9727},[1736,14496,14497],{"class":4866},"    const",[1736,14499,14500],{"class":1918}," newProducts",[1736,14502,4911],{"class":4866},[1736,14504,14245],{"class":1912},[1736,14506,14507],{"class":2674},"filter",[1736,14509,7751],{"class":1912},[1736,14511,11],{"class":5036},[1736,14513,10208],{"class":4866},[1736,14515,14256],{"class":1912},[1736,14517,14518],{"class":4866},"!==",[1736,14520,14521],{"class":1912}," product.id)\n",[1736,14523,14524],{"class":1738,"line":9739},[1736,14525,1747],{"emptyLinePlaceholder":790},[1736,14527,14528,14531],{"class":1738,"line":9750},[1736,14529,14530],{"class":2674},"    setProducts",[1736,14532,14533],{"class":1912},"(newProducts)\n",[1736,14535,14536],{"class":1738,"line":9761},[1736,14537,1971],{"class":1912},[1736,14539,14540],{"class":1738,"line":9767},[1736,14541,1747],{"emptyLinePlaceholder":790},[1736,14543,14544,14546,14549,14551,14553,14555,14557,14559,14561],{"class":1738,"line":9778},[1736,14545,7824],{"class":4866},[1736,14547,14548],{"class":1918}," contextValue",[1736,14550,1087],{"class":4866},[1736,14552,13787],{"class":2674},[1736,14554,6657],{"class":1912},[1736,14556,13713],{"class":2674},[1736,14558,13932],{"class":1912},[1736,14560,5062],{"class":4866},[1736,14562,4914],{"class":1912},[1736,14564,14565],{"class":1738,"line":9799},[1736,14566,14567],{"class":1912},"    cart: products,\n",[1736,14569,14570],{"class":1738,"line":9804},[1736,14571,14572],{"class":1912},"    addProductToCart,\n",[1736,14574,14575],{"class":1738,"line":9814},[1736,14576,14577],{"class":1912},"    removeProductFromCart\n",[1736,14579,14580],{"class":1738,"line":9826},[1736,14581,1971],{"class":1912},[1736,14583,14584],{"class":1738,"line":9838},[1736,14585,1747],{"emptyLinePlaceholder":790},[1736,14587,14588,14590,14592,14595,14598,14600,14603,14605],{"class":1738,"line":9850},[1736,14589,6685],{"class":4866},[1736,14591,10193],{"class":1912},[1736,14593,14594],{"class":1918},"context.Provider",[1736,14596,14597],{"class":2674}," value",[1736,14599,5062],{"class":4866},[1736,14601,14602],{"class":1912},"{contextValue}>{children}\u003C/",[1736,14604,14594],{"class":1918},[1736,14606,6663],{"class":1912},[1736,14608,14609],{"class":1738,"line":9856},[1736,14610,1976],{"class":1912},[23,14612,14614],{"id":14613},"displaying-the-cart","Displaying the Cart",[11,14616,14617],{},"We can now use our provider component to wrap our cart components so that anything inside the provider has access to the products in the cart. This means we could have the shopping cart itself as well as component in the header that shows the cart icon with a number beside it so you know how many items are in the cart. Because you want both of these components to have access to the state of the cart we would wrap them in the provider component.",[11,14619,14620,14621,14626],{},"We start of by creating the context with the type of Product which we have already created. Feel free to check out ",[15,14622,14625],{"href":14623,"rel":14624},"https://bit.dev/learn-bit-react/ecommerce/entity/product?version=1.0.7",[19],"the code for the product types"," yourself to see how it works but it is simply just types that every product should have such as an id, title, text, price, etc.",[299,14628,14630],{"className":4894,"code":14629,"language":4896,"meta":307,"style":307},"const contextObject = CreateCartContext\u003CProduct>();\n",[179,14631,14632],{"__ignoreMap":307},[1736,14633,14634,14636,14639,14641,14643,14645,14647],{"class":1738,"line":1739},[1736,14635,5029],{"class":4866},[1736,14637,14638],{"class":1918}," contextObject",[1736,14640,4911],{"class":4866},[1736,14642,13985],{"class":2674},[1736,14644,6657],{"class":1912},[1736,14646,8081],{"class":2674},[1736,14648,14649],{"class":1912},">();\n",[11,14651,14652],{},"I am now going to create two mock components just so we cans see the cart works in isolation before actually using it inside your app or inside another component. This is our way of testing our component works and does exactly what we want it to do. We do this by using compositions which I am using Bit for however feel free to just create regular components inside your app to test it out.",[11,14654,14655],{},"The first mock component we need is the cart display component. This should use map over the context and print out the title and price of the product as well as add a button to remove the product from the cart. For this example we are not adding the image or anything else but just showing a basic example of how it works.",[299,14657,14659],{"className":8734,"code":14658,"language":8736,"meta":307,"style":307},"const MockCartDisplay = () => {\n  const context = useContext(contextObject)\n\n  return (\n    \u003Cdiv>\n      \u003Ch2>Cart:\u003C/h2>\n      {context.cart.map((cartItem, index) => {\n        return (\n          \u003Cdiv key={index}>\n            \u003Ch2>{cartItem.item.title}\u003C/h2>\n            \u003Cp> {cartItem.item.price}\u003C/p>\n            \u003Cbutton\n              className=\"bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded p-20\"\n              onClick={() => context.removeProductFromCart(cartItem.item)}\n            >\n              Remove from Cart\n            \u003C/button>\n          \u003C/div>\n        )\n      })}\n    \u003C/div>\n  )\n}\n",[179,14660,14661,14676,14691,14695,14701,14709,14722,14745,14752,14767,14781,14794,14801,14811,14832,14837,14842,14852,14861,14866,14871,14879,14883],{"__ignoreMap":307},[1736,14662,14663,14665,14668,14670,14672,14674],{"class":1738,"line":1739},[1736,14664,5029],{"class":4866},[1736,14666,14667],{"class":2674}," MockCartDisplay",[1736,14669,4911],{"class":4866},[1736,14671,7010],{"class":1912},[1736,14673,7013],{"class":4866},[1736,14675,4914],{"class":1912},[1736,14677,14678,14680,14683,14685,14688],{"class":1738,"line":748},[1736,14679,7824],{"class":4866},[1736,14681,14682],{"class":1918}," context",[1736,14684,4911],{"class":4866},[1736,14686,14687],{"class":2674}," useContext",[1736,14689,14690],{"class":1912},"(contextObject)\n",[1736,14692,14693],{"class":1738,"line":756},[1736,14694,1747],{"emptyLinePlaceholder":790},[1736,14696,14697,14699],{"class":1738,"line":1755},[1736,14698,6685],{"class":4866},[1736,14700,6688],{"class":1912},[1736,14702,14703,14705,14707],{"class":1738,"line":1761},[1736,14704,6693],{"class":1912},[1736,14706,6697],{"class":6696},[1736,14708,6663],{"class":1912},[1736,14710,14711,14713,14715,14718,14720],{"class":1738,"line":1767},[1736,14712,6710],{"class":1912},[1736,14714,23],{"class":6696},[1736,14716,14717],{"class":1912},">Cart:\u003C/",[1736,14719,23],{"class":6696},[1736,14721,6663],{"class":1912},[1736,14723,14724,14727,14729,14731,14734,14736,14739,14741,14743],{"class":1738,"line":1772},[1736,14725,14726],{"class":1912},"      {context.cart.",[1736,14728,14366],{"class":2674},[1736,14730,14369],{"class":1912},[1736,14732,14733],{"class":5036},"cartItem",[1736,14735,829],{"class":1912},[1736,14737,14738],{"class":5036},"index",[1736,14740,8939],{"class":1912},[1736,14742,7013],{"class":4866},[1736,14744,4914],{"class":1912},[1736,14746,14747,14750],{"class":1738,"line":1778},[1736,14748,14749],{"class":4866},"        return",[1736,14751,6688],{"class":1912},[1736,14753,14754,14757,14759,14762,14764],{"class":1738,"line":1784},[1736,14755,14756],{"class":1912},"          \u003C",[1736,14758,6697],{"class":6696},[1736,14760,14761],{"class":2674}," key",[1736,14763,5062],{"class":4866},[1736,14765,14766],{"class":1912},"{index}>\n",[1736,14768,14769,14772,14774,14777,14779],{"class":1738,"line":1790},[1736,14770,14771],{"class":1912},"            \u003C",[1736,14773,23],{"class":6696},[1736,14775,14776],{"class":1912},">{cartItem.item.title}\u003C/",[1736,14778,23],{"class":6696},[1736,14780,6663],{"class":1912},[1736,14782,14783,14785,14787,14790,14792],{"class":1738,"line":1796},[1736,14784,14771],{"class":1912},[1736,14786,11],{"class":6696},[1736,14788,14789],{"class":1912},"> {cartItem.item.price}\u003C/",[1736,14791,11],{"class":6696},[1736,14793,6663],{"class":1912},[1736,14795,14796,14798],{"class":1738,"line":2353},[1736,14797,14771],{"class":1912},[1736,14799,14800],{"class":6696},"button\n",[1736,14802,14803,14806,14808],{"class":1738,"line":2358},[1736,14804,14805],{"class":2674},"              className",[1736,14807,5062],{"class":4866},[1736,14809,14810],{"class":1935},"\"bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded p-20\"\n",[1736,14812,14813,14816,14818,14821,14823,14826,14829],{"class":1738,"line":2364},[1736,14814,14815],{"class":2674},"              onClick",[1736,14817,5062],{"class":4866},[1736,14819,14820],{"class":1912},"{() ",[1736,14822,7013],{"class":4866},[1736,14824,14825],{"class":1912}," context.",[1736,14827,14828],{"class":2674},"removeProductFromCart",[1736,14830,14831],{"class":1912},"(cartItem.item)}\n",[1736,14833,14834],{"class":1738,"line":2370},[1736,14835,14836],{"class":1912},"            >\n",[1736,14838,14839],{"class":1738,"line":2376},[1736,14840,14841],{"class":1912},"              Remove from Cart\n",[1736,14843,14844,14847,14850],{"class":1738,"line":2381},[1736,14845,14846],{"class":1912},"            \u003C/",[1736,14848,14849],{"class":6696},"button",[1736,14851,6663],{"class":1912},[1736,14853,14854,14857,14859],{"class":1738,"line":2387},[1736,14855,14856],{"class":1912},"          \u003C/",[1736,14858,6697],{"class":6696},[1736,14860,6663],{"class":1912},[1736,14862,14863],{"class":1738,"line":2393},[1736,14864,14865],{"class":1912},"        )\n",[1736,14867,14868],{"class":1738,"line":2398},[1736,14869,14870],{"class":1912},"      })}\n",[1736,14872,14873,14875,14877],{"class":1738,"line":2404},[1736,14874,6744],{"class":1912},[1736,14876,6697],{"class":6696},[1736,14878,6663],{"class":1912},[1736,14880,14881],{"class":1738,"line":6959},[1736,14882,6753],{"class":1912},[1736,14884,14885],{"class":1738,"line":7296},[1736,14886,1976],{"class":1912},[11,14888,14889],{},"We then need a mock component that updates the context when we add a new product. We start by using the context and passing in the contextObject. We then create an item. We could have manually created a JSON with some data but instead we will get some products randomly from an API. This is one we have created previously and just gives us some mock data to use.",[11,14891,14892],{},"Our addProductToCart function uses the context addProductToCart function passing in the item that we get from our mock API as well as the quantity.",[11,14894,14895],{},"Finally we return a button component with an onClick function that calls the addProductToCart function.",[299,14897,14899],{"className":8734,"code":14898,"language":8736,"meta":307,"style":307},"const MockUpdateContextComponent = () => {\n  const context = useContext(contextObject)\n\n  const item = Product.fromApiObject(\n    mockProductFromApi[Math.floor(Math.random() * 9)]\n  )\n\n  function addProductToCart() {\n    context.addProductToCart({ item, quantity: 1 })\n  }\n\n  return (\n    \u003Cdiv>\n      \u003Cbutton\n        className=\"bg-blue-500 mt-2 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded p-20\"\n        onClick={() => addProductToCart()}\n      >\n        Add to Cart\n      \u003C/button>\n    \u003C/div>\n  )\n}\n",[179,14900,14901,14916,14928,14932,14950,14975,14979,14983,14991,15006,15010,15014,15020,15028,15034,15043,15059,15064,15069,15077,15085,15089],{"__ignoreMap":307},[1736,14902,14903,14905,14908,14910,14912,14914],{"class":1738,"line":1739},[1736,14904,5029],{"class":4866},[1736,14906,14907],{"class":2674}," MockUpdateContextComponent",[1736,14909,4911],{"class":4866},[1736,14911,7010],{"class":1912},[1736,14913,7013],{"class":4866},[1736,14915,4914],{"class":1912},[1736,14917,14918,14920,14922,14924,14926],{"class":1738,"line":748},[1736,14919,7824],{"class":4866},[1736,14921,14682],{"class":1918},[1736,14923,4911],{"class":4866},[1736,14925,14687],{"class":2674},[1736,14927,14690],{"class":1912},[1736,14929,14930],{"class":1738,"line":756},[1736,14931,1747],{"emptyLinePlaceholder":790},[1736,14933,14934,14936,14939,14941,14944,14947],{"class":1738,"line":1755},[1736,14935,7824],{"class":4866},[1736,14937,14938],{"class":1918}," item",[1736,14940,4911],{"class":4866},[1736,14942,14943],{"class":1912}," Product.",[1736,14945,14946],{"class":2674},"fromApiObject",[1736,14948,14949],{"class":1912},"(\n",[1736,14951,14952,14955,14958,14961,14964,14966,14969,14972],{"class":1738,"line":1761},[1736,14953,14954],{"class":1912},"    mockProductFromApi[Math.",[1736,14956,14957],{"class":2674},"floor",[1736,14959,14960],{"class":1912},"(Math.",[1736,14962,14963],{"class":2674},"random",[1736,14965,7840],{"class":1912},[1736,14967,14968],{"class":4866},"*",[1736,14970,14971],{"class":1918}," 9",[1736,14973,14974],{"class":1912},")]\n",[1736,14976,14977],{"class":1738,"line":1767},[1736,14978,6753],{"class":1912},[1736,14980,14981],{"class":1738,"line":1772},[1736,14982,1747],{"emptyLinePlaceholder":790},[1736,14984,14985,14987,14989],{"class":1738,"line":1778},[1736,14986,9807],{"class":4866},[1736,14988,14276],{"class":2674},[1736,14990,6680],{"class":1912},[1736,14992,14993,14996,14999,15002,15004],{"class":1738,"line":1784},[1736,14994,14995],{"class":1912},"    context.",[1736,14997,14998],{"class":2674},"addProductToCart",[1736,15000,15001],{"class":1912},"({ item, quantity: ",[1736,15003,10249],{"class":1918},[1736,15005,10691],{"class":1912},[1736,15007,15008],{"class":1738,"line":1790},[1736,15009,1971],{"class":1912},[1736,15011,15012],{"class":1738,"line":1796},[1736,15013,1747],{"emptyLinePlaceholder":790},[1736,15015,15016,15018],{"class":1738,"line":2353},[1736,15017,6685],{"class":4866},[1736,15019,6688],{"class":1912},[1736,15021,15022,15024,15026],{"class":1738,"line":2358},[1736,15023,6693],{"class":1912},[1736,15025,6697],{"class":6696},[1736,15027,6663],{"class":1912},[1736,15029,15030,15032],{"class":1738,"line":2364},[1736,15031,6710],{"class":1912},[1736,15033,14800],{"class":6696},[1736,15035,15036,15038,15040],{"class":1738,"line":2370},[1736,15037,9125],{"class":2674},[1736,15039,5062],{"class":4866},[1736,15041,15042],{"class":1935},"\"bg-blue-500 mt-2 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded p-20\"\n",[1736,15044,15045,15048,15050,15052,15054,15056],{"class":1738,"line":2376},[1736,15046,15047],{"class":2674},"        onClick",[1736,15049,5062],{"class":4866},[1736,15051,14820],{"class":1912},[1736,15053,7013],{"class":4866},[1736,15055,14276],{"class":2674},[1736,15057,15058],{"class":1912},"()}\n",[1736,15060,15061],{"class":1738,"line":2381},[1736,15062,15063],{"class":1912},"      >\n",[1736,15065,15066],{"class":1738,"line":2387},[1736,15067,15068],{"class":1912},"        Add to Cart\n",[1736,15070,15071,15073,15075],{"class":1738,"line":2393},[1736,15072,8261],{"class":1912},[1736,15074,14849],{"class":6696},[1736,15076,6663],{"class":1912},[1736,15078,15079,15081,15083],{"class":1738,"line":2398},[1736,15080,6744],{"class":1912},[1736,15082,6697],{"class":6696},[1736,15084,6663],{"class":1912},[1736,15086,15087],{"class":1738,"line":2404},[1736,15088,6753],{"class":1912},[1736,15090,15091],{"class":1738,"line":6959},[1736,15092,1976],{"class":1912},[11,15094,15095],{},"We can now create our component that is responsible for showing how the cart works. This component will show the cart display component as well as the button to add a product to the cart and both of these components as they need access to the cart context will be wrapped in the context provider. The context passed into the provider is the contextObject.",[299,15097,15099],{"className":4894,"code":15098,"language":4896,"meta":307,"style":307},"export const BasicCartUsage = () => {\n  return (\n    \u003CCartContextProvider\u003CProduct> context={contextObject}>\n      \u003CMockCartDisplay />\n      \u003CMockUpdateContextComponent />\n    \u003C/CartContextProvider>\n  );\n};\n",[179,15100,15101,15118,15124,15145,15154,15163,15171,15175],{"__ignoreMap":307},[1736,15102,15103,15105,15107,15110,15112,15114,15116],{"class":1738,"line":1739},[1736,15104,6632],{"class":4866},[1736,15106,7002],{"class":4866},[1736,15108,15109],{"class":2674}," BasicCartUsage",[1736,15111,4911],{"class":4866},[1736,15113,7010],{"class":1912},[1736,15115,7013],{"class":4866},[1736,15117,4914],{"class":1912},[1736,15119,15120,15122],{"class":1738,"line":748},[1736,15121,6685],{"class":4866},[1736,15123,6688],{"class":1912},[1736,15125,15126,15128,15131,15133,15135,15137,15140,15142],{"class":1738,"line":756},[1736,15127,6693],{"class":1912},[1736,15129,15130],{"class":1918},"CartContextProvider",[1736,15132,6657],{"class":1912},[1736,15134,8081],{"class":2674},[1736,15136,13932],{"class":1912},[1736,15138,15139],{"class":2674},"context",[1736,15141,5062],{"class":4866},[1736,15143,15144],{"class":1912},"{contextObject}>\n",[1736,15146,15147,15149,15152],{"class":1738,"line":1755},[1736,15148,6710],{"class":1912},[1736,15150,15151],{"class":1918},"MockCartDisplay",[1736,15153,6739],{"class":1912},[1736,15155,15156,15158,15161],{"class":1738,"line":1761},[1736,15157,6710],{"class":1912},[1736,15159,15160],{"class":1918},"MockUpdateContextComponent",[1736,15162,6739],{"class":1912},[1736,15164,15165,15167,15169],{"class":1738,"line":1767},[1736,15166,6744],{"class":1912},[1736,15168,15130],{"class":1918},[1736,15170,6663],{"class":1912},[1736,15172,15173],{"class":1738,"line":1772},[1736,15174,7956],{"class":1912},[1736,15176,15177],{"class":1738,"line":1778},[1736,15178,15179],{"class":1912},"};\n",[11,15181,15182],{},"This component now works as it should and can be used in your app by simply installing the component and passing in your own contextObject and creating your own cart display and update context button.",[23,15184,15186],{"id":15185},"install-and-use","Install and Use",[11,15188,15189],{},"To install the component and play around with it yourself you can use npm or yarn and install it in your React app like you would with any other package:",[299,15191,15193],{"className":2665,"code":15192,"language":2667,"meta":307,"style":307},"yarn add @learn-bit-react/ecommerce.ui.cart.cart-context\n",[179,15194,15195],{"__ignoreMap":307},[1736,15196,15197,15199,15201],{"class":1738,"line":1739},[1736,15198,6575],{"class":2674},[1736,15200,2681],{"class":1935},[1736,15202,15203],{"class":1935}," @learn-bit-react/ecommerce.ui.cart.cart-context\n",[11,15205,15206],{},"Then import it into the app and use it just like we did. You can add your own display components or copy the mock ones to see how it works and then modify it to how you like.",[11,15208,15209,15210,15215],{},"Check out a ",[15,15211,15214],{"href":15212,"rel":15213},"https://github.com/debs-obrien/react-cart-context/blob/main/src/App.js",[19],"simple example I have created here"," to see it in use in a React app.",[138,15217,15219],{"id":15218},"example-usage","Example Usage",[299,15221,15223],{"className":4894,"code":15222,"language":4896,"meta":307,"style":307},"import {\n  CartContextProvider,\n  CreateCartContext\n} from '@learn-bit-react/ecommerce.ui.cart.cart-context'\n\nconst contextObject = CreateCartContext\u003CProduct>();\n\n// create your mock components here like above\n\nexport MyCart(){\n  return (\n    \u003CCartContextProvider\u003CProduct> context={contextObject}>\n      \u003CMockCartDisplay />\n      \u003CMockUpdateContextComponent />\n    \u003C/CartContextProvider>\n  );\n};\n",[179,15224,15225,15231,15236,15241,15250,15254,15270,15274,15279,15283,15293,15299,15317,15325,15333,15341,15345],{"__ignoreMap":307},[1736,15226,15227,15229],{"class":1738,"line":1739},[1736,15228,4996],{"class":4866},[1736,15230,4914],{"class":1912},[1736,15232,15233],{"class":1738,"line":748},[1736,15234,15235],{"class":1912},"  CartContextProvider,\n",[1736,15237,15238],{"class":1738,"line":756},[1736,15239,15240],{"class":1912},"  CreateCartContext\n",[1736,15242,15243,15245,15247],{"class":1738,"line":1755},[1736,15244,6871],{"class":1912},[1736,15246,5002],{"class":4866},[1736,15248,15249],{"class":1935}," '@learn-bit-react/ecommerce.ui.cart.cart-context'\n",[1736,15251,15252],{"class":1738,"line":1761},[1736,15253,1747],{"emptyLinePlaceholder":790},[1736,15255,15256,15258,15260,15262,15264,15266,15268],{"class":1738,"line":1767},[1736,15257,5029],{"class":4866},[1736,15259,14638],{"class":1918},[1736,15261,4911],{"class":4866},[1736,15263,13985],{"class":2674},[1736,15265,6657],{"class":1912},[1736,15267,8081],{"class":2674},[1736,15269,14649],{"class":1912},[1736,15271,15272],{"class":1738,"line":1772},[1736,15273,1747],{"emptyLinePlaceholder":790},[1736,15275,15276],{"class":1738,"line":1778},[1736,15277,15278],{"class":6820},"// create your mock components here like above\n",[1736,15280,15281],{"class":1738,"line":1784},[1736,15282,1747],{"emptyLinePlaceholder":790},[1736,15284,15285,15287,15290],{"class":1738,"line":1790},[1736,15286,6632],{"class":4866},[1736,15288,15289],{"class":2674}," MyCart",[1736,15291,15292],{"class":1912},"(){\n",[1736,15294,15295,15297],{"class":1738,"line":1796},[1736,15296,6685],{"class":4866},[1736,15298,6688],{"class":1912},[1736,15300,15301,15303,15305,15307,15309,15311,15313,15315],{"class":1738,"line":2353},[1736,15302,6693],{"class":1912},[1736,15304,15130],{"class":1918},[1736,15306,6657],{"class":1912},[1736,15308,8081],{"class":2674},[1736,15310,13932],{"class":1912},[1736,15312,15139],{"class":2674},[1736,15314,5062],{"class":4866},[1736,15316,15144],{"class":1912},[1736,15318,15319,15321,15323],{"class":1738,"line":2358},[1736,15320,6710],{"class":1912},[1736,15322,15151],{"class":1918},[1736,15324,6739],{"class":1912},[1736,15326,15327,15329,15331],{"class":1738,"line":2364},[1736,15328,6710],{"class":1912},[1736,15330,15160],{"class":1918},[1736,15332,6739],{"class":1912},[1736,15334,15335,15337,15339],{"class":1738,"line":2370},[1736,15336,6744],{"class":1912},[1736,15338,15130],{"class":1918},[1736,15340,6663],{"class":1912},[1736,15342,15343],{"class":1738,"line":2376},[1736,15344,7956],{"class":1912},[1736,15346,15347],{"class":1738,"line":2381},[1736,15348,15179],{"class":1912},[23,15350,5706],{"id":5705},[70,15352,15353,15359,15365,15372,15379,15386,15392],{},[73,15354,15355],{},[15,15356,15358],{"href":13656,"rel":15357},[19],"Context Component Code",[73,15360,15361],{},[15,15362,15364],{"href":14030,"rel":15363},[19],"Context Provider Code",[73,15366,15367],{},[15,15368,15371],{"href":15369,"rel":15370},"https://bit.dev/learn-bit-react/ecommerce/ui/cart/cart-context/~code/cart-context.composition.tsx",[19],"Cart Composition Code",[73,15373,15374],{},[15,15375,15378],{"href":15376,"rel":15377},"https://bit.dev/learn-bit-react/ecommerce/ui/cart/cart-context",[19],"Cart Docs",[73,15380,15381],{},[15,15382,15385],{"href":15383,"rel":15384},"https://bit.dev/learn-bit-react/ecommerce/ui/cart/cart-context/~compositions",[19],"Interactive Cart Composition",[73,15387,15388],{},[15,15389,15391],{"href":13631,"rel":15390},[19],"Shoe Store using the Cart component",[73,15393,15394],{},[15,15395,15397],{"href":15212,"rel":15396},[19],"Simple example in React App",[2011,15399,15400],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":15402},[15403,15404,15405,15406,15409],{"id":13649,"depth":748,"text":13650},{"id":14023,"depth":748,"text":14024},{"id":14613,"depth":748,"text":14614},{"id":15185,"depth":748,"text":15186,"children":15407},[15408],{"id":15218,"depth":756,"text":15219},{"id":5705,"depth":748,"text":5706},"2022-02-02","Creating a generic cart component that can add and remove a product from the cart using React Context. This component can then be used in any other component or app to create more specific cart components.","v1643820029/debbie.codes/blog/2022/cart-context_2x_qlqg5q",{},"/blog/building-cart-context-component",{"title":13623,"description":15411},"blog/building-cart-context-component",[5221],"gd64NJyLRUs0Zi18ZR1KDzTb9JAedbn41z5LT-cvqP4",{"id":15420,"title":15421,"body":15422,"canonical":17208,"date":17209,"description":15426,"extension":786,"featured":787,"image":788,"meta":17210,"navigation":790,"ogimage":788,"path":17211,"provider":788,"published":790,"seo":17212,"stem":17213,"tags":17214,"url":788,"__hash__":17215},"blog/blog/building-your-first-mcp-server.md","Building Your First MCP Server - A Beginner's Tutorial",{"type":8,"value":15423,"toc":17193},[15424,15427,15429,15432,15436,15439,15450,15454,15457,15471,15475,15478,15508,15512,15515,15544,15548,15555,16228,16232,16235,16238,16271,16274,16287,16291,16294,16576,16579,17001,17005,17008,17023,17082,17086,17089,17121,17125,17128,17145,17147,17150,17153,17157,17187,17190],[11,15425,15426],{},"Learn how to build your first Model Context Protocol (MCP) server from scratch. This beginner-friendly tutorial walks you through the fundamentals of MCP and guides you step-by-step through creating your own server to enhance AI applications.",[23,15428,5794],{"id":5793},[11,15430,15431],{},"Model Context Protocol (MCP) is an emerging standard that enables AI applications to securely access external data sources and tools. Think of it as a bridge between AI models and the real world - allowing them to interact with databases, APIs, file systems, and other resources in a controlled and secure way.",[23,15433,15435],{"id":15434},"getting-started","Getting Started",[11,15437,15438],{},"In this tutorial, we'll build a simple MCP server that can:",[70,15440,15441,15444,15447],{},[73,15442,15443],{},"Provide context about files in a directory",[73,15445,15446],{},"Execute simple commands",[73,15448,15449],{},"Return structured data to AI applications",[23,15451,15453],{"id":15452},"prerequisites","Prerequisites",[11,15455,15456],{},"Before we begin, make sure you have:",[70,15458,15459,15462,15465,15468],{},[73,15460,15461],{},"Node.js (version 18 or higher)",[73,15463,15464],{},"Basic knowledge of JavaScript/TypeScript",[73,15466,15467],{},"A text editor or IDE",[73,15469,15470],{},"Terminal access",[23,15472,15474],{"id":15473},"setting-up-your-project","Setting Up Your Project",[11,15476,15477],{},"Let's start by creating a new directory for our MCP server:",[299,15479,15481],{"className":2665,"code":15480,"language":2667,"meta":307,"style":307},"mkdir my-first-mcp-server\ncd my-first-mcp-server\nnpm init -y\n",[179,15482,15483,15491,15498],{"__ignoreMap":307},[1736,15484,15485,15488],{"class":1738,"line":1739},[1736,15486,15487],{"class":2674},"mkdir",[1736,15489,15490],{"class":1935}," my-first-mcp-server\n",[1736,15492,15493,15496],{"class":1738,"line":748},[1736,15494,15495],{"class":1918},"cd",[1736,15497,15490],{"class":1935},[1736,15499,15500,15502,15505],{"class":1738,"line":756},[1736,15501,6565],{"class":2674},[1736,15503,15504],{"class":1935}," init",[1736,15506,15507],{"class":1918}," -y\n",[23,15509,15511],{"id":15510},"installing-dependencies","Installing Dependencies",[11,15513,15514],{},"We'll need the MCP SDK to build our server:",[299,15516,15518],{"className":2665,"code":15517,"language":2667,"meta":307,"style":307},"npm install @modelcontextprotocol/sdk\nnpm install --save-dev typescript @types/node\n",[179,15519,15520,15529],{"__ignoreMap":307},[1736,15521,15522,15524,15526],{"class":1738,"line":1739},[1736,15523,6565],{"class":2674},[1736,15525,4973],{"class":1935},[1736,15527,15528],{"class":1935}," @modelcontextprotocol/sdk\n",[1736,15530,15531,15533,15535,15538,15541],{"class":1738,"line":748},[1736,15532,6565],{"class":2674},[1736,15534,4973],{"class":1935},[1736,15536,15537],{"class":1918}," --save-dev",[1736,15539,15540],{"class":1935}," typescript",[1736,15542,15543],{"class":1935}," @types/node\n",[23,15545,15547],{"id":15546},"creating-your-first-mcp-server","Creating Your First MCP Server",[11,15549,15550,15551,15554],{},"Create a new file called ",[179,15552,15553],{},"server.ts"," in your project directory:",[299,15556,15560],{"className":15557,"code":15558,"language":15559,"meta":307,"style":307},"language-typescript shiki shiki-themes github-light github-dark","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n  CallToolRequestSchema,\n  ListToolsRequestSchema,\n  Tool,\n} from '@modelcontextprotocol/sdk/types.js';\n\nclass MyMCPServer {\n  private server: Server;\n\n  constructor() {\n    this.server = new Server(\n      {\n        name: 'my-first-mcp-server',\n        version: '0.1.0',\n      },\n      {\n        capabilities: {\n          tools: {},\n        },\n      }\n    );\n\n    this.setupToolHandlers();\n  }\n\n  private setupToolHandlers() {\n    // List available tools\n    this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n      return {\n        tools: [\n          {\n            name: 'hello',\n            description: 'Say hello to the world',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                name: {\n                  type: 'string',\n                  description: 'Name to greet',\n                },\n              },\n            },\n          },\n        ],\n      };\n    });\n\n    // Handle tool calls\n    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      const { name, arguments: args } = request.params;\n\n      switch (name) {\n        case 'hello':\n          const nameToGreet = args?.name || 'World';\n          return {\n            content: [\n              {\n                type: 'text',\n                text: `Hello, ${nameToGreet}! This is your first MCP server responding.`,\n              },\n            ],\n          };\n        \n        default:\n          throw new Error(`Unknown tool: ${name}`);\n      }\n    });\n  }\n\n  async run() {\n    const transport = new StdioServerTransport();\n    await this.server.connect(transport);\n    console.error('MCP server running on stdio');\n  }\n}\n\nconst server = new MyMCPServer();\nserver.run().catch(console.error);\n","typescript",[179,15561,15562,15576,15590,15596,15601,15606,15611,15622,15626,15636,15651,15655,15662,15679,15684,15694,15704,15709,15713,15718,15723,15728,15732,15737,15741,15753,15757,15761,15770,15775,15797,15804,15809,15814,15824,15834,15839,15849,15854,15859,15869,15879,15884,15889,15894,15899,15904,15909,15914,15918,15923,15947,15973,15977,15985,15996,16016,16022,16027,16032,16042,16058,16062,16067,16072,16077,16084,16107,16111,16115,16119,16123,16133,16149,16165,16180,16185,16190,16195,16210],{"__ignoreMap":307},[1736,15563,15564,15566,15569,15571,15574],{"class":1738,"line":1739},[1736,15565,4996],{"class":4866},[1736,15567,15568],{"class":1912}," { Server } ",[1736,15570,5002],{"class":4866},[1736,15572,15573],{"class":1935}," '@modelcontextprotocol/sdk/server/index.js'",[1736,15575,7682],{"class":1912},[1736,15577,15578,15580,15583,15585,15588],{"class":1738,"line":748},[1736,15579,4996],{"class":4866},[1736,15581,15582],{"class":1912}," { StdioServerTransport } ",[1736,15584,5002],{"class":4866},[1736,15586,15587],{"class":1935}," '@modelcontextprotocol/sdk/server/stdio.js'",[1736,15589,7682],{"class":1912},[1736,15591,15592,15594],{"class":1738,"line":756},[1736,15593,4996],{"class":4866},[1736,15595,4914],{"class":1912},[1736,15597,15598],{"class":1738,"line":1755},[1736,15599,15600],{"class":1912},"  CallToolRequestSchema,\n",[1736,15602,15603],{"class":1738,"line":1761},[1736,15604,15605],{"class":1912},"  ListToolsRequestSchema,\n",[1736,15607,15608],{"class":1738,"line":1767},[1736,15609,15610],{"class":1912},"  Tool,\n",[1736,15612,15613,15615,15617,15620],{"class":1738,"line":1772},[1736,15614,6871],{"class":1912},[1736,15616,5002],{"class":4866},[1736,15618,15619],{"class":1935}," '@modelcontextprotocol/sdk/types.js'",[1736,15621,7682],{"class":1912},[1736,15623,15624],{"class":1738,"line":1778},[1736,15625,1747],{"emptyLinePlaceholder":790},[1736,15627,15628,15631,15634],{"class":1738,"line":1784},[1736,15629,15630],{"class":4866},"class",[1736,15632,15633],{"class":2674}," MyMCPServer",[1736,15635,4914],{"class":1912},[1736,15637,15638,15641,15644,15646,15649],{"class":1738,"line":1790},[1736,15639,15640],{"class":4866},"  private",[1736,15642,15643],{"class":5036}," server",[1736,15645,1087],{"class":4866},[1736,15647,15648],{"class":2674}," Server",[1736,15650,7682],{"class":1912},[1736,15652,15653],{"class":1738,"line":1796},[1736,15654,1747],{"emptyLinePlaceholder":790},[1736,15656,15657,15660],{"class":1738,"line":2353},[1736,15658,15659],{"class":4866},"  constructor",[1736,15661,6680],{"class":1912},[1736,15663,15664,15667,15670,15672,15675,15677],{"class":1738,"line":2358},[1736,15665,15666],{"class":1918},"    this",[1736,15668,15669],{"class":1912},".server ",[1736,15671,5062],{"class":4866},[1736,15673,15674],{"class":4866}," new",[1736,15676,15648],{"class":2674},[1736,15678,14949],{"class":1912},[1736,15680,15681],{"class":1738,"line":2364},[1736,15682,15683],{"class":1912},"      {\n",[1736,15685,15686,15689,15692],{"class":1738,"line":2370},[1736,15687,15688],{"class":1912},"        name: ",[1736,15690,15691],{"class":1935},"'my-first-mcp-server'",[1736,15693,1939],{"class":1912},[1736,15695,15696,15699,15702],{"class":1738,"line":2376},[1736,15697,15698],{"class":1912},"        version: ",[1736,15700,15701],{"class":1935},"'0.1.0'",[1736,15703,1939],{"class":1912},[1736,15705,15706],{"class":1738,"line":2381},[1736,15707,15708],{"class":1912},"      },\n",[1736,15710,15711],{"class":1738,"line":2387},[1736,15712,15683],{"class":1912},[1736,15714,15715],{"class":1738,"line":2393},[1736,15716,15717],{"class":1912},"        capabilities: {\n",[1736,15719,15720],{"class":1738,"line":2398},[1736,15721,15722],{"class":1912},"          tools: {},\n",[1736,15724,15725],{"class":1738,"line":2404},[1736,15726,15727],{"class":1912},"        },\n",[1736,15729,15730],{"class":1738,"line":6959},[1736,15731,14448],{"class":1912},[1736,15733,15734],{"class":1738,"line":7296},[1736,15735,15736],{"class":1912},"    );\n",[1736,15738,15739],{"class":1738,"line":7305},[1736,15740,1747],{"emptyLinePlaceholder":790},[1736,15742,15743,15745,15747,15750],{"class":1738,"line":7310},[1736,15744,15666],{"class":1918},[1736,15746,891],{"class":1912},[1736,15748,15749],{"class":2674},"setupToolHandlers",[1736,15751,15752],{"class":1912},"();\n",[1736,15754,15755],{"class":1738,"line":9659},[1736,15756,1971],{"class":1912},[1736,15758,15759],{"class":1738,"line":9680},[1736,15760,1747],{"emptyLinePlaceholder":790},[1736,15762,15763,15765,15768],{"class":1738,"line":9699},[1736,15764,15640],{"class":4866},[1736,15766,15767],{"class":2674}," setupToolHandlers",[1736,15769,6680],{"class":1912},[1736,15771,15772],{"class":1738,"line":9704},[1736,15773,15774],{"class":6820},"    // List available tools\n",[1736,15776,15777,15779,15782,15785,15788,15791,15793,15795],{"class":1738,"line":9715},[1736,15778,15666],{"class":1918},[1736,15780,15781],{"class":1912},".server.",[1736,15783,15784],{"class":2674},"setRequestHandler",[1736,15786,15787],{"class":1912},"(ListToolsRequestSchema, ",[1736,15789,15790],{"class":4866},"async",[1736,15792,7010],{"class":1912},[1736,15794,7013],{"class":4866},[1736,15796,4914],{"class":1912},[1736,15798,15799,15802],{"class":1738,"line":9727},[1736,15800,15801],{"class":4866},"      return",[1736,15803,4914],{"class":1912},[1736,15805,15806],{"class":1738,"line":9739},[1736,15807,15808],{"class":1912},"        tools: [\n",[1736,15810,15811],{"class":1738,"line":9750},[1736,15812,15813],{"class":1912},"          {\n",[1736,15815,15816,15819,15822],{"class":1738,"line":9761},[1736,15817,15818],{"class":1912},"            name: ",[1736,15820,15821],{"class":1935},"'hello'",[1736,15823,1939],{"class":1912},[1736,15825,15826,15829,15832],{"class":1738,"line":9767},[1736,15827,15828],{"class":1912},"            description: ",[1736,15830,15831],{"class":1935},"'Say hello to the world'",[1736,15833,1939],{"class":1912},[1736,15835,15836],{"class":1738,"line":9778},[1736,15837,15838],{"class":1912},"            inputSchema: {\n",[1736,15840,15841,15844,15847],{"class":1738,"line":9799},[1736,15842,15843],{"class":1912},"              type: ",[1736,15845,15846],{"class":1935},"'object'",[1736,15848,1939],{"class":1912},[1736,15850,15851],{"class":1738,"line":9804},[1736,15852,15853],{"class":1912},"              properties: {\n",[1736,15855,15856],{"class":1738,"line":9814},[1736,15857,15858],{"class":1912},"                name: {\n",[1736,15860,15861,15864,15867],{"class":1738,"line":9826},[1736,15862,15863],{"class":1912},"                  type: ",[1736,15865,15866],{"class":1935},"'string'",[1736,15868,1939],{"class":1912},[1736,15870,15871,15874,15877],{"class":1738,"line":9838},[1736,15872,15873],{"class":1912},"                  description: ",[1736,15875,15876],{"class":1935},"'Name to greet'",[1736,15878,1939],{"class":1912},[1736,15880,15881],{"class":1738,"line":9850},[1736,15882,15883],{"class":1912},"                },\n",[1736,15885,15886],{"class":1738,"line":9856},[1736,15887,15888],{"class":1912},"              },\n",[1736,15890,15891],{"class":1738,"line":9861},[1736,15892,15893],{"class":1912},"            },\n",[1736,15895,15896],{"class":1738,"line":9870},[1736,15897,15898],{"class":1912},"          },\n",[1736,15900,15901],{"class":1738,"line":9881},[1736,15902,15903],{"class":1912},"        ],\n",[1736,15905,15906],{"class":1738,"line":9892},[1736,15907,15908],{"class":1912},"      };\n",[1736,15910,15911],{"class":1738,"line":9903},[1736,15912,15913],{"class":1912},"    });\n",[1736,15915,15916],{"class":1738,"line":9908},[1736,15917,1747],{"emptyLinePlaceholder":790},[1736,15919,15920],{"class":1738,"line":9913},[1736,15921,15922],{"class":6820},"    // Handle tool calls\n",[1736,15924,15925,15927,15929,15931,15934,15936,15938,15941,15943,15945],{"class":1738,"line":9918},[1736,15926,15666],{"class":1918},[1736,15928,15781],{"class":1912},[1736,15930,15784],{"class":2674},[1736,15932,15933],{"class":1912},"(CallToolRequestSchema, ",[1736,15935,15790],{"class":4866},[1736,15937,1095],{"class":1912},[1736,15939,15940],{"class":5036},"request",[1736,15942,8939],{"class":1912},[1736,15944,7013],{"class":4866},[1736,15946,4914],{"class":1912},[1736,15948,15949,15951,15953,15956,15958,15961,15963,15966,15968,15970],{"class":1738,"line":9931},[1736,15950,14312],{"class":4866},[1736,15952,7827],{"class":1912},[1736,15954,15955],{"class":1918},"name",[1736,15957,829],{"class":1912},[1736,15959,15960],{"class":5036},"arguments",[1736,15962,3065],{"class":1912},[1736,15964,15965],{"class":1918},"args",[1736,15967,7832],{"class":1912},[1736,15969,5062],{"class":4866},[1736,15971,15972],{"class":1912}," request.params;\n",[1736,15974,15975],{"class":1738,"line":9938},[1736,15976,1747],{"emptyLinePlaceholder":790},[1736,15978,15979,15982],{"class":1738,"line":9945},[1736,15980,15981],{"class":4866},"      switch",[1736,15983,15984],{"class":1912}," (name) {\n",[1736,15986,15987,15990,15993],{"class":1738,"line":9950},[1736,15988,15989],{"class":4866},"        case",[1736,15991,15992],{"class":1935}," 'hello'",[1736,15994,15995],{"class":1912},":\n",[1736,15997,15998,16001,16004,16006,16009,16011,16014],{"class":1738,"line":9955},[1736,15999,16000],{"class":4866},"          const",[1736,16002,16003],{"class":1918}," nameToGreet",[1736,16005,4911],{"class":4866},[1736,16007,16008],{"class":1912}," args?.name ",[1736,16010,7792],{"class":4866},[1736,16012,16013],{"class":1935}," 'World'",[1736,16015,7682],{"class":1912},[1736,16017,16018,16020],{"class":1738,"line":9962},[1736,16019,14427],{"class":4866},[1736,16021,4914],{"class":1912},[1736,16023,16024],{"class":1738,"line":9975},[1736,16025,16026],{"class":1912},"            content: [\n",[1736,16028,16029],{"class":1738,"line":9990},[1736,16030,16031],{"class":1912},"              {\n",[1736,16033,16034,16037,16040],{"class":1738,"line":9995},[1736,16035,16036],{"class":1912},"                type: ",[1736,16038,16039],{"class":1935},"'text'",[1736,16041,1939],{"class":1912},[1736,16043,16044,16047,16050,16053,16056],{"class":1738,"line":10004},[1736,16045,16046],{"class":1912},"                text: ",[1736,16048,16049],{"class":1935},"`Hello, ${",[1736,16051,16052],{"class":1912},"nameToGreet",[1736,16054,16055],{"class":1935},"}! This is your first MCP server responding.`",[1736,16057,1939],{"class":1912},[1736,16059,16060],{"class":1738,"line":10011},[1736,16061,15888],{"class":1912},[1736,16063,16064],{"class":1738,"line":10020},[1736,16065,16066],{"class":1912},"            ],\n",[1736,16068,16069],{"class":1738,"line":10029},[1736,16070,16071],{"class":1912},"          };\n",[1736,16073,16074],{"class":1738,"line":10038},[1736,16075,16076],{"class":1912},"        \n",[1736,16078,16079,16082],{"class":1738,"line":10047},[1736,16080,16081],{"class":4866},"        default",[1736,16083,15995],{"class":1912},[1736,16085,16086,16089,16091,16094,16096,16099,16101,16104],{"class":1738,"line":10056},[1736,16087,16088],{"class":4866},"          throw",[1736,16090,15674],{"class":4866},[1736,16092,16093],{"class":2674}," Error",[1736,16095,7751],{"class":1912},[1736,16097,16098],{"class":1935},"`Unknown tool: ${",[1736,16100,15955],{"class":1912},[1736,16102,16103],{"class":1935},"}`",[1736,16105,16106],{"class":1912},");\n",[1736,16108,16109],{"class":1738,"line":10065},[1736,16110,14448],{"class":1912},[1736,16112,16113],{"class":1738,"line":10074},[1736,16114,15913],{"class":1912},[1736,16116,16117],{"class":1738,"line":10079},[1736,16118,1971],{"class":1912},[1736,16120,16121],{"class":1738,"line":10094},[1736,16122,1747],{"emptyLinePlaceholder":790},[1736,16124,16125,16128,16131],{"class":1738,"line":10099},[1736,16126,16127],{"class":4866},"  async",[1736,16129,16130],{"class":2674}," run",[1736,16132,6680],{"class":1912},[1736,16134,16135,16137,16140,16142,16144,16147],{"class":1738,"line":10108},[1736,16136,14497],{"class":4866},[1736,16138,16139],{"class":1918}," transport",[1736,16141,4911],{"class":4866},[1736,16143,15674],{"class":4866},[1736,16145,16146],{"class":2674}," StdioServerTransport",[1736,16148,15752],{"class":1912},[1736,16150,16151,16154,16157,16159,16162],{"class":1738,"line":10117},[1736,16152,16153],{"class":4866},"    await",[1736,16155,16156],{"class":1918}," this",[1736,16158,15781],{"class":1912},[1736,16160,16161],{"class":2674},"connect",[1736,16163,16164],{"class":1912},"(transport);\n",[1736,16166,16167,16170,16173,16175,16178],{"class":1738,"line":10122},[1736,16168,16169],{"class":1912},"    console.",[1736,16171,16172],{"class":2674},"error",[1736,16174,7751],{"class":1912},[1736,16176,16177],{"class":1935},"'MCP server running on stdio'",[1736,16179,16106],{"class":1912},[1736,16181,16183],{"class":1738,"line":16182},76,[1736,16184,1971],{"class":1912},[1736,16186,16188],{"class":1738,"line":16187},77,[1736,16189,1976],{"class":1912},[1736,16191,16193],{"class":1738,"line":16192},78,[1736,16194,1747],{"emptyLinePlaceholder":790},[1736,16196,16198,16200,16202,16204,16206,16208],{"class":1738,"line":16197},79,[1736,16199,5029],{"class":4866},[1736,16201,15643],{"class":1918},[1736,16203,4911],{"class":4866},[1736,16205,15674],{"class":4866},[1736,16207,15633],{"class":2674},[1736,16209,15752],{"class":1912},[1736,16211,16213,16216,16219,16222,16225],{"class":1738,"line":16212},80,[1736,16214,16215],{"class":1912},"server.",[1736,16217,16218],{"class":2674},"run",[1736,16220,16221],{"class":1912},"().",[1736,16223,16224],{"class":2674},"catch",[1736,16226,16227],{"class":1912},"(console.error);\n",[23,16229,16231],{"id":16230},"testing-your-server","Testing Your Server",[11,16233,16234],{},"To test your MCP server, you'll need an MCP client. You can use the MCP Inspector tool or integrate it with Claude Desktop.",[11,16236,16237],{},"First, compile your TypeScript:",[299,16239,16241],{"className":2665,"code":16240,"language":2667,"meta":307,"style":307},"npx tsc server.ts --target es2022 --module node16 --moduleResolution node16\n",[179,16242,16243],{"__ignoreMap":307},[1736,16244,16245,16247,16250,16253,16256,16259,16262,16265,16268],{"class":1738,"line":1739},[1736,16246,2675],{"class":2674},[1736,16248,16249],{"class":1935}," tsc",[1736,16251,16252],{"class":1935}," server.ts",[1736,16254,16255],{"class":1918}," --target",[1736,16257,16258],{"class":1935}," es2022",[1736,16260,16261],{"class":1918}," --module",[1736,16263,16264],{"class":1935}," node16",[1736,16266,16267],{"class":1918}," --moduleResolution",[1736,16269,16270],{"class":1935}," node16\n",[11,16272,16273],{},"Then run your server:",[299,16275,16277],{"className":2665,"code":16276,"language":2667,"meta":307,"style":307},"node server.js\n",[179,16278,16279],{"__ignoreMap":307},[1736,16280,16281,16284],{"class":1738,"line":1739},[1736,16282,16283],{"class":2674},"node",[1736,16285,16286],{"class":1935}," server.js\n",[23,16288,16290],{"id":16289},"adding-more-functionality","Adding More Functionality",[11,16292,16293],{},"Let's enhance our server with more useful tools:",[299,16295,16297],{"className":15557,"code":16296,"language":15559,"meta":307,"style":307},"// Add this to your tools list\n{\n  name: 'get-time',\n  description: 'Get the current time',\n  inputSchema: {\n    type: 'object',\n    properties: {},\n  },\n},\n{\n  name: 'calculate',\n  description: 'Perform basic mathematical calculations',\n  inputSchema: {\n    type: 'object',\n    properties: {\n      operation: {\n        type: 'string',\n        enum: ['add', 'subtract', 'multiply', 'divide'],\n        description: 'Mathematical operation to perform',\n      },\n      a: {\n        type: 'number',\n        description: 'First number',\n      },\n      b: {\n        type: 'number',\n        description: 'Second number',\n      },\n    },\n    required: ['operation', 'a', 'b'],\n  },\n}\n",[179,16298,16299,16304,16308,16320,16332,16339,16350,16358,16362,16367,16371,16382,16393,16399,16409,16415,16422,16432,16461,16473,16477,16484,16495,16506,16510,16517,16527,16538,16542,16546,16568,16572],{"__ignoreMap":307},[1736,16300,16301],{"class":1738,"line":1739},[1736,16302,16303],{"class":6820},"// Add this to your tools list\n",[1736,16305,16306],{"class":1738,"line":748},[1736,16307,1913],{"class":1912},[1736,16309,16310,16313,16315,16318],{"class":1738,"line":756},[1736,16311,16312],{"class":2674},"  name",[1736,16314,3065],{"class":1912},[1736,16316,16317],{"class":1935},"'get-time'",[1736,16319,1939],{"class":1912},[1736,16321,16322,16325,16327,16330],{"class":1738,"line":1755},[1736,16323,16324],{"class":2674},"  description",[1736,16326,3065],{"class":1912},[1736,16328,16329],{"class":1935},"'Get the current time'",[1736,16331,1939],{"class":1912},[1736,16333,16334,16337],{"class":1738,"line":1761},[1736,16335,16336],{"class":2674},"  inputSchema",[1736,16338,1922],{"class":1912},[1736,16340,16341,16344,16346,16348],{"class":1738,"line":1767},[1736,16342,16343],{"class":2674},"    type",[1736,16345,3065],{"class":1912},[1736,16347,15846],{"class":1935},[1736,16349,1939],{"class":1912},[1736,16351,16352,16355],{"class":1738,"line":1772},[1736,16353,16354],{"class":2674},"    properties",[1736,16356,16357],{"class":1912},": {},\n",[1736,16359,16360],{"class":1738,"line":1778},[1736,16361,4929],{"class":1912},[1736,16363,16364],{"class":1738,"line":1784},[1736,16365,16366],{"class":1912},"},\n",[1736,16368,16369],{"class":1738,"line":1790},[1736,16370,1913],{"class":1912},[1736,16372,16373,16375,16377,16380],{"class":1738,"line":1796},[1736,16374,16312],{"class":2674},[1736,16376,3065],{"class":1912},[1736,16378,16379],{"class":1935},"'calculate'",[1736,16381,1939],{"class":1912},[1736,16383,16384,16386,16388,16391],{"class":1738,"line":2353},[1736,16385,16324],{"class":2674},[1736,16387,3065],{"class":1912},[1736,16389,16390],{"class":1935},"'Perform basic mathematical calculations'",[1736,16392,1939],{"class":1912},[1736,16394,16395,16397],{"class":1738,"line":2358},[1736,16396,16336],{"class":2674},[1736,16398,1922],{"class":1912},[1736,16400,16401,16403,16405,16407],{"class":1738,"line":2364},[1736,16402,16343],{"class":2674},[1736,16404,3065],{"class":1912},[1736,16406,15846],{"class":1935},[1736,16408,1939],{"class":1912},[1736,16410,16411,16413],{"class":1738,"line":2370},[1736,16412,16354],{"class":2674},[1736,16414,1922],{"class":1912},[1736,16416,16417,16420],{"class":1738,"line":2376},[1736,16418,16419],{"class":2674},"      operation",[1736,16421,1922],{"class":1912},[1736,16423,16424,16426,16428,16430],{"class":1738,"line":2381},[1736,16425,9135],{"class":2674},[1736,16427,3065],{"class":1912},[1736,16429,15866],{"class":1935},[1736,16431,1939],{"class":1912},[1736,16433,16434,16437,16440,16443,16445,16448,16450,16453,16455,16458],{"class":1738,"line":2387},[1736,16435,16436],{"class":2674},"        enum",[1736,16438,16439],{"class":1912},": [",[1736,16441,16442],{"class":1935},"'add'",[1736,16444,829],{"class":1912},[1736,16446,16447],{"class":1935},"'subtract'",[1736,16449,829],{"class":1912},[1736,16451,16452],{"class":1935},"'multiply'",[1736,16454,829],{"class":1912},[1736,16456,16457],{"class":1935},"'divide'",[1736,16459,16460],{"class":1912},"],\n",[1736,16462,16463,16466,16468,16471],{"class":1738,"line":2393},[1736,16464,16465],{"class":2674},"        description",[1736,16467,3065],{"class":1912},[1736,16469,16470],{"class":1935},"'Mathematical operation to perform'",[1736,16472,1939],{"class":1912},[1736,16474,16475],{"class":1738,"line":2398},[1736,16476,15708],{"class":1912},[1736,16478,16479,16482],{"class":1738,"line":2404},[1736,16480,16481],{"class":2674},"      a",[1736,16483,1922],{"class":1912},[1736,16485,16486,16488,16490,16493],{"class":1738,"line":6959},[1736,16487,9135],{"class":2674},[1736,16489,3065],{"class":1912},[1736,16491,16492],{"class":1935},"'number'",[1736,16494,1939],{"class":1912},[1736,16496,16497,16499,16501,16504],{"class":1738,"line":7296},[1736,16498,16465],{"class":2674},[1736,16500,3065],{"class":1912},[1736,16502,16503],{"class":1935},"'First number'",[1736,16505,1939],{"class":1912},[1736,16507,16508],{"class":1738,"line":7305},[1736,16509,15708],{"class":1912},[1736,16511,16512,16515],{"class":1738,"line":7310},[1736,16513,16514],{"class":2674},"      b",[1736,16516,1922],{"class":1912},[1736,16518,16519,16521,16523,16525],{"class":1738,"line":9659},[1736,16520,9135],{"class":2674},[1736,16522,3065],{"class":1912},[1736,16524,16492],{"class":1935},[1736,16526,1939],{"class":1912},[1736,16528,16529,16531,16533,16536],{"class":1738,"line":9680},[1736,16530,16465],{"class":2674},[1736,16532,3065],{"class":1912},[1736,16534,16535],{"class":1935},"'Second number'",[1736,16537,1939],{"class":1912},[1736,16539,16540],{"class":1738,"line":9699},[1736,16541,15708],{"class":1912},[1736,16543,16544],{"class":1738,"line":9704},[1736,16545,8553],{"class":1912},[1736,16547,16548,16551,16553,16556,16558,16561,16563,16566],{"class":1738,"line":9715},[1736,16549,16550],{"class":2674},"    required",[1736,16552,16439],{"class":1912},[1736,16554,16555],{"class":1935},"'operation'",[1736,16557,829],{"class":1912},[1736,16559,16560],{"class":1935},"'a'",[1736,16562,829],{"class":1912},[1736,16564,16565],{"class":1935},"'b'",[1736,16567,16460],{"class":1912},[1736,16569,16570],{"class":1738,"line":9727},[1736,16571,4929],{"class":1912},[1736,16573,16574],{"class":1738,"line":9739},[1736,16575,1976],{"class":1912},[11,16577,16578],{},"And handle these new tools:",[299,16580,16582],{"className":15557,"code":16581,"language":15559,"meta":307,"style":307},"case 'get-time':\n  return {\n    content: [\n      {\n        type: 'text',\n        text: `Current time: ${new Date().toISOString()}`,\n      },\n    ],\n  };\n\ncase 'calculate':\n  const { operation, a, b } = args as {\n    operation: string;\n    a: number;\n    b: number;\n  };\n  \n  let result: number;\n  switch (operation) {\n    case 'add':\n      result = a + b;\n      break;\n    case 'subtract':\n      result = a - b;\n      break;\n    case 'multiply':\n      result = a * b;\n      break;\n    case 'divide':\n      if (b === 0) throw new Error('Division by zero');\n      result = a / b;\n      break;\n    default:\n      throw new Error(`Unknown operation: ${operation}`);\n  }\n  \n  return {\n    content: [\n      {\n        type: 'text',\n        text: `${a} ${operation} ${b} = ${result}`,\n      },\n    ],\n  };\n",[179,16583,16584,16594,16600,16605,16609,16618,16644,16648,16652,16657,16661,16670,16699,16710,16721,16732,16736,16741,16755,16763,16773,16788,16795,16804,16816,16822,16831,16843,16849,16858,16886,16898,16904,16911,16931,16935,16939,16945,16949,16953,16961,16989,16993,16997],{"__ignoreMap":307},[1736,16585,16586,16589,16592],{"class":1738,"line":1739},[1736,16587,16588],{"class":4866},"case",[1736,16590,16591],{"class":1935}," 'get-time'",[1736,16593,15995],{"class":1912},[1736,16595,16596,16598],{"class":1738,"line":748},[1736,16597,6685],{"class":4866},[1736,16599,4914],{"class":1912},[1736,16601,16602],{"class":1738,"line":756},[1736,16603,16604],{"class":1912},"    content: [\n",[1736,16606,16607],{"class":1738,"line":1755},[1736,16608,15683],{"class":1912},[1736,16610,16611,16614,16616],{"class":1738,"line":1761},[1736,16612,16613],{"class":1912},"        type: ",[1736,16615,16039],{"class":1935},[1736,16617,1939],{"class":1912},[1736,16619,16620,16623,16626,16629,16632,16634,16637,16640,16642],{"class":1738,"line":1767},[1736,16621,16622],{"class":1912},"        text: ",[1736,16624,16625],{"class":1935},"`Current time: ${",[1736,16627,16628],{"class":4866},"new",[1736,16630,16631],{"class":2674}," Date",[1736,16633,16221],{"class":1935},[1736,16635,16636],{"class":2674},"toISOString",[1736,16638,16639],{"class":1935},"()",[1736,16641,16103],{"class":1935},[1736,16643,1939],{"class":1912},[1736,16645,16646],{"class":1738,"line":1772},[1736,16647,15708],{"class":1912},[1736,16649,16650],{"class":1738,"line":1778},[1736,16651,1949],{"class":1912},[1736,16653,16654],{"class":1738,"line":1784},[1736,16655,16656],{"class":1912},"  };\n",[1736,16658,16659],{"class":1738,"line":1790},[1736,16660,1747],{"emptyLinePlaceholder":790},[1736,16662,16663,16665,16668],{"class":1738,"line":1796},[1736,16664,16588],{"class":4866},[1736,16666,16667],{"class":1935}," 'calculate'",[1736,16669,15995],{"class":1912},[1736,16671,16672,16674,16676,16679,16681,16683,16685,16688,16690,16692,16695,16697],{"class":1738,"line":2353},[1736,16673,7824],{"class":4866},[1736,16675,7827],{"class":1912},[1736,16677,16678],{"class":1918},"operation",[1736,16680,829],{"class":1912},[1736,16682,15],{"class":1918},[1736,16684,829],{"class":1912},[1736,16686,16687],{"class":1918},"b",[1736,16689,7832],{"class":1912},[1736,16691,5062],{"class":4866},[1736,16693,16694],{"class":1912}," args ",[1736,16696,7843],{"class":4866},[1736,16698,4914],{"class":1912},[1736,16700,16701,16704,16706,16708],{"class":1738,"line":2358},[1736,16702,16703],{"class":5036},"    operation",[1736,16705,1087],{"class":4866},[1736,16707,6841],{"class":1918},[1736,16709,7682],{"class":1912},[1736,16711,16712,16715,16717,16719],{"class":1738,"line":2364},[1736,16713,16714],{"class":5036},"    a",[1736,16716,1087],{"class":4866},[1736,16718,8833],{"class":1918},[1736,16720,7682],{"class":1912},[1736,16722,16723,16726,16728,16730],{"class":1738,"line":2370},[1736,16724,16725],{"class":5036},"    b",[1736,16727,1087],{"class":4866},[1736,16729,8833],{"class":1918},[1736,16731,7682],{"class":1912},[1736,16733,16734],{"class":1738,"line":2376},[1736,16735,16656],{"class":1912},[1736,16737,16738],{"class":1738,"line":2381},[1736,16739,16740],{"class":1912},"  \n",[1736,16742,16743,16746,16749,16751,16753],{"class":1738,"line":2387},[1736,16744,16745],{"class":4866},"  let",[1736,16747,16748],{"class":1912}," result",[1736,16750,1087],{"class":4866},[1736,16752,8833],{"class":1918},[1736,16754,7682],{"class":1912},[1736,16756,16757,16760],{"class":1738,"line":2393},[1736,16758,16759],{"class":4866},"  switch",[1736,16761,16762],{"class":1912}," (operation) {\n",[1736,16764,16765,16768,16771],{"class":1738,"line":2398},[1736,16766,16767],{"class":4866},"    case",[1736,16769,16770],{"class":1935}," 'add'",[1736,16772,15995],{"class":1912},[1736,16774,16775,16778,16780,16783,16785],{"class":1738,"line":2404},[1736,16776,16777],{"class":1912},"      result ",[1736,16779,5062],{"class":4866},[1736,16781,16782],{"class":1912}," a ",[1736,16784,9343],{"class":4866},[1736,16786,16787],{"class":1912}," b;\n",[1736,16789,16790,16793],{"class":1738,"line":6959},[1736,16791,16792],{"class":4866},"      break",[1736,16794,7682],{"class":1912},[1736,16796,16797,16799,16802],{"class":1738,"line":7296},[1736,16798,16767],{"class":4866},[1736,16800,16801],{"class":1935}," 'subtract'",[1736,16803,15995],{"class":1912},[1736,16805,16806,16808,16810,16812,16814],{"class":1738,"line":7305},[1736,16807,16777],{"class":1912},[1736,16809,5062],{"class":4866},[1736,16811,16782],{"class":1912},[1736,16813,9419],{"class":4866},[1736,16815,16787],{"class":1912},[1736,16817,16818,16820],{"class":1738,"line":7310},[1736,16819,16792],{"class":4866},[1736,16821,7682],{"class":1912},[1736,16823,16824,16826,16829],{"class":1738,"line":9659},[1736,16825,16767],{"class":4866},[1736,16827,16828],{"class":1935}," 'multiply'",[1736,16830,15995],{"class":1912},[1736,16832,16833,16835,16837,16839,16841],{"class":1738,"line":9680},[1736,16834,16777],{"class":1912},[1736,16836,5062],{"class":4866},[1736,16838,16782],{"class":1912},[1736,16840,14968],{"class":4866},[1736,16842,16787],{"class":1912},[1736,16844,16845,16847],{"class":1738,"line":9699},[1736,16846,16792],{"class":4866},[1736,16848,7682],{"class":1912},[1736,16850,16851,16853,16856],{"class":1738,"line":9704},[1736,16852,16767],{"class":4866},[1736,16854,16855],{"class":1935}," 'divide'",[1736,16857,15995],{"class":1912},[1736,16859,16860,16862,16865,16867,16870,16872,16875,16877,16879,16881,16884],{"class":1738,"line":9715},[1736,16861,14351],{"class":4866},[1736,16863,16864],{"class":1912}," (b ",[1736,16866,7786],{"class":4866},[1736,16868,16869],{"class":1918}," 0",[1736,16871,8939],{"class":1912},[1736,16873,16874],{"class":4866},"throw",[1736,16876,15674],{"class":4866},[1736,16878,16093],{"class":2674},[1736,16880,7751],{"class":1912},[1736,16882,16883],{"class":1935},"'Division by zero'",[1736,16885,16106],{"class":1912},[1736,16887,16888,16890,16892,16894,16896],{"class":1738,"line":9727},[1736,16889,16777],{"class":1912},[1736,16891,5062],{"class":4866},[1736,16893,16782],{"class":1912},[1736,16895,1066],{"class":4866},[1736,16897,16787],{"class":1912},[1736,16899,16900,16902],{"class":1738,"line":9739},[1736,16901,16792],{"class":4866},[1736,16903,7682],{"class":1912},[1736,16905,16906,16909],{"class":1738,"line":9750},[1736,16907,16908],{"class":4866},"    default",[1736,16910,15995],{"class":1912},[1736,16912,16913,16916,16918,16920,16922,16925,16927,16929],{"class":1738,"line":9761},[1736,16914,16915],{"class":4866},"      throw",[1736,16917,15674],{"class":4866},[1736,16919,16093],{"class":2674},[1736,16921,7751],{"class":1912},[1736,16923,16924],{"class":1935},"`Unknown operation: ${",[1736,16926,16678],{"class":1912},[1736,16928,16103],{"class":1935},[1736,16930,16106],{"class":1912},[1736,16932,16933],{"class":1738,"line":9767},[1736,16934,1971],{"class":1912},[1736,16936,16937],{"class":1738,"line":9778},[1736,16938,16740],{"class":1912},[1736,16940,16941,16943],{"class":1738,"line":9799},[1736,16942,6685],{"class":4866},[1736,16944,4914],{"class":1912},[1736,16946,16947],{"class":1738,"line":9804},[1736,16948,16604],{"class":1912},[1736,16950,16951],{"class":1738,"line":9814},[1736,16952,15683],{"class":1912},[1736,16954,16955,16957,16959],{"class":1738,"line":9826},[1736,16956,16613],{"class":1912},[1736,16958,16039],{"class":1935},[1736,16960,1939],{"class":1912},[1736,16962,16963,16965,16968,16970,16973,16975,16977,16979,16982,16985,16987],{"class":1738,"line":9838},[1736,16964,16622],{"class":1912},[1736,16966,16967],{"class":1935},"`${",[1736,16969,15],{"class":1912},[1736,16971,16972],{"class":1935},"} ${",[1736,16974,16678],{"class":1912},[1736,16976,16972],{"class":1935},[1736,16978,16687],{"class":1912},[1736,16980,16981],{"class":1935},"} = ${",[1736,16983,16984],{"class":1912},"result",[1736,16986,16103],{"class":1935},[1736,16988,1939],{"class":1912},[1736,16990,16991],{"class":1738,"line":9850},[1736,16992,15708],{"class":1912},[1736,16994,16995],{"class":1738,"line":9856},[1736,16996,1949],{"class":1912},[1736,16998,16999],{"class":1738,"line":9861},[1736,17000,16656],{"class":1912},[23,17002,17004],{"id":17003},"integrating-with-claude-desktop","Integrating with Claude Desktop",[11,17006,17007],{},"To use your MCP server with Claude Desktop, add it to your configuration file:",[11,17009,17010,17013,17014,17017,17013,17020],{},[58,17011,17012],{},"On macOS:"," ",[179,17015,17016],{},"~/Library/Application Support/Claude/claude_desktop_config.json",[58,17018,17019],{},"On Windows:",[179,17021,17022],{},"%APPDATA%/Claude/claude_desktop_config.json",[299,17024,17026],{"className":1903,"code":17025,"language":1905,"meta":307,"style":307},"{\n  \"mcpServers\": {\n    \"my-first-mcp-server\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/your/server.js\"]\n    }\n  }\n}\n",[179,17027,17028,17032,17039,17046,17058,17070,17074,17078],{"__ignoreMap":307},[1736,17029,17030],{"class":1738,"line":1739},[1736,17031,1913],{"class":1912},[1736,17033,17034,17037],{"class":1738,"line":748},[1736,17035,17036],{"class":1918},"  \"mcpServers\"",[1736,17038,1922],{"class":1912},[1736,17040,17041,17044],{"class":1738,"line":756},[1736,17042,17043],{"class":1918},"    \"my-first-mcp-server\"",[1736,17045,1922],{"class":1912},[1736,17047,17048,17051,17053,17056],{"class":1738,"line":1755},[1736,17049,17050],{"class":1918},"      \"command\"",[1736,17052,3065],{"class":1912},[1736,17054,17055],{"class":1935},"\"node\"",[1736,17057,1939],{"class":1912},[1736,17059,17060,17063,17065,17068],{"class":1738,"line":1761},[1736,17061,17062],{"class":1918},"      \"args\"",[1736,17064,16439],{"class":1912},[1736,17066,17067],{"class":1935},"\"/path/to/your/server.js\"",[1736,17069,8420],{"class":1912},[1736,17071,17072],{"class":1738,"line":1767},[1736,17073,9853],{"class":1912},[1736,17075,17076],{"class":1738,"line":1772},[1736,17077,1971],{"class":1912},[1736,17079,17080],{"class":1738,"line":1778},[1736,17081,1976],{"class":1912},[23,17083,17085],{"id":17084},"best-practices","Best Practices",[11,17087,17088],{},"When building MCP servers, keep these best practices in mind:",[2260,17090,17091,17097,17103,17109,17115],{},[73,17092,17093,17096],{},[58,17094,17095],{},"Security First",": Always validate inputs and sanitize outputs",[73,17098,17099,17102],{},[58,17100,17101],{},"Clear Documentation",": Provide comprehensive descriptions for your tools",[73,17104,17105,17108],{},[58,17106,17107],{},"Error Handling",": Implement robust error handling for all operations",[73,17110,17111,17114],{},[58,17112,17113],{},"Performance",": Be mindful of response times and resource usage",[73,17116,17117,17120],{},[58,17118,17119],{},"Logging",": Implement proper logging for debugging and monitoring",[23,17122,17124],{"id":17123},"next-steps","Next Steps",[11,17126,17127],{},"Now that you have a basic MCP server running, you can:",[70,17129,17130,17133,17136,17139,17142],{},[73,17131,17132],{},"Add file system operations",[73,17134,17135],{},"Integrate with external APIs",[73,17137,17138],{},"Implement database connections",[73,17140,17141],{},"Create more complex data processing tools",[73,17143,17144],{},"Build domain-specific functionality",[23,17146,3294],{"id":3293},[11,17148,17149],{},"Building your first MCP server opens up exciting possibilities for AI integration. You've learned the fundamentals and created a working server that can respond to tool calls and provide useful functionality.",[11,17151,17152],{},"The MCP ecosystem is rapidly evolving, and there are many opportunities to create powerful integrations that enhance AI applications. Keep experimenting and building!",[23,17154,17156],{"id":17155},"resources","Resources",[70,17158,17159,17166,17173,17180],{},[73,17160,17161],{},[15,17162,17165],{"href":17163,"rel":17164},"https://modelcontextprotocol.io/",[19],"MCP Official Documentation",[73,17167,17168],{},[15,17169,17172],{"href":17170,"rel":17171},"https://github.com/modelcontextprotocol/servers",[19],"MCP SDK GitHub Repository",[73,17174,17175],{},[15,17176,17179],{"href":17177,"rel":17178},"https://docs.anthropic.com/claude/docs/mcp",[19],"Claude Desktop MCP Integration Guide",[73,17181,17182],{},[15,17183,17186],{"href":17184,"rel":17185},"https://github.com/modelcontextprotocol/servers/tree/main/src",[19],"MCP Server Examples",[11,17188,17189],{},"Happy building! 🚀",[2011,17191,17192],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":17194},[17195,17196,17197,17198,17199,17200,17201,17202,17203,17204,17205,17206,17207],{"id":5793,"depth":748,"text":5794},{"id":15434,"depth":748,"text":15435},{"id":15452,"depth":748,"text":15453},{"id":15473,"depth":748,"text":15474},{"id":15510,"depth":748,"text":15511},{"id":15546,"depth":748,"text":15547},{"id":16230,"depth":748,"text":16231},{"id":16289,"depth":748,"text":16290},{"id":17003,"depth":748,"text":17004},{"id":17084,"depth":748,"text":17085},{"id":17123,"depth":748,"text":17124},{"id":3293,"depth":748,"text":3294},{"id":17155,"depth":748,"text":17156},"https://dev.to/debs_obrien/building-your-first-mcp-server-a-beginners-tutorial-5fag","2024-07-03",{"loading":3458},"/blog/building-your-first-mcp-server",{"title":15421,"description":15426},"blog/building-your-first-mcp-server",[3321,795],"dRLqNXun2D8QJPXJZWZu0JBMmJjvAUejhZt-OB4z7hI",{"id":17217,"title":17218,"body":17219,"canonical":17208,"date":19850,"description":19851,"extension":786,"featured":790,"image":788,"meta":19852,"navigation":790,"ogimage":788,"path":19853,"provider":788,"published":790,"seo":19854,"stem":19855,"tags":19856,"url":788,"__hash__":19857},"blog/blog/building-your-first-mcp-server-a-beginners-tutorial.md","Building Your First MCP Server: A Beginners Tutorial",{"type":8,"value":17220,"toc":19810},[17221,17224,17227,17230,17235,17249,17254,17265,17268,17272,17275,17281,17286,17292,17295,17299,17302,17306,17309,17335,17339,17342,17355,17359,17370,17451,17459,17463,17466,17470,17473,17486,17490,17493,17507,17513,17551,17555,17562,17566,17611,17615,17658,17661,17665,17672,17800,17805,17834,17839,17856,17860,17863,17890,17895,17919,17923,17929,18138,18145,18149,18152,18156,18159,18184,18187,18201,18207,18211,18240,18246,18251,18259,18262,18266,18275,18279,18282,18296,18300,18303,18714,18718,18737,18740,18744,18747,18751,18797,18804,18900,18904,18930,18933,18939,18964,18970,18974,18977,18982,18990,18995,19003,19008,19016,19021,19029,19033,19039,19492,19496,19499,19503,19508,19535,19540,19567,19572,19599,19603,19611,19613,19618,19623,19637,19642,19668,19673,19675,19679,19684,19728,19733,19750,19755,19777,19787,19792,19799,19807],[11,17222,17223],{},"Have you ever wanted your AI assistant to access real-time data? Model Context Protocol (MCP) servers make this possible, and they're surprisingly simple to build and use!",[11,17225,17226],{},"You may have already seen my videos and posts on using the Playwright MCP to go to a website and generate test ideas and then generate actual Playwright tests after first interacting with the site. Or how I used it to go shopping for me. This is the power of MCPs. It gives the AI agents tools to be able to do things such as connect to a browser or as in the GitHub MCP, create pull requests etc.",[11,17228,17229],{},"In this tutorial, you'll create a weather server that connects AI agents like GitHub Copilot to live weather data. We will use TypeScript for this demo but you can build MCP servers in other languages, links at the end of the post. By the end, you'll be able to ask your AI for weather information in any city and get real, up-to-date responses.",[11,17231,17232],{},[58,17233,17234],{},"What you'll learn:",[70,17236,17237,17240,17243,17246],{},[73,17238,17239],{},"How to build an MCP server from scratch using the TypeScript SDK",[73,17241,17242],{},"Connect it to a real weather API",[73,17244,17245],{},"Integrate it with VS Code and GitHub Copilot",[73,17247,17248],{},"Test and debug your server",[11,17250,17251],{},[58,17252,17253],{},"What you'll need:",[70,17255,17256,17259,17262],{},[73,17257,17258],{},"Basic TypeScript/JavaScript knowledge",[73,17260,17261],{},"Node.js installed on your machine",[73,17263,17264],{},"VS Code (optional, but recommended)",[11,17266,17267],{},"Let's get started!",[23,17269,17271],{"id":17270},"what-is-an-mcp-server","What is an MCP Server?",[11,17273,17274],{},"Model Context Protocol (MCP) servers are bridges that connect AI agents to external tools and data sources. Think of them as translators that help AI understand and interact with real-world applications.",[11,17276,17277,17280],{},[58,17278,17279],{},"The Problem:"," When you ask GitHub Copilot for weather information in VS Code, you'll get a response like this:",[1713,17282,17283],{},[11,17284,17285],{},"\"I don't have access to real-time weather data or weather APIs through the available tools in this coding environment.\"",[11,17287,17288,17291],{},[58,17289,17290],{},"The Solution:"," MCP servers provide the missing link, giving AI agents the tools they need to access live data and perform real actions.",[11,17293,17294],{},"Our weather server will act as a tool that any MCP-compatible AI can call to get current weather information for any city worldwide.",[23,17296,17298],{"id":17297},"step-1-project-setup","Step 1: Project Setup",[11,17300,17301],{},"Let's create a new project and set up our development environment.",[138,17303,17305],{"id":17304},"_1-initialize-the-project","1. Initialize the Project",[11,17307,17308],{},"Create a new directory and initialize it with npm:",[299,17310,17312],{"className":2665,"code":17311,"language":2667,"meta":307,"style":307},"mkdir mcp-weather-server\ncd mcp-weather-server\nnpm init -y\n",[179,17313,17314,17321,17327],{"__ignoreMap":307},[1736,17315,17316,17318],{"class":1738,"line":1739},[1736,17317,15487],{"class":2674},[1736,17319,17320],{"class":1935}," mcp-weather-server\n",[1736,17322,17323,17325],{"class":1738,"line":748},[1736,17324,15495],{"class":1918},[1736,17326,17320],{"class":1935},[1736,17328,17329,17331,17333],{"class":1738,"line":756},[1736,17330,6565],{"class":2674},[1736,17332,15504],{"class":1935},[1736,17334,15507],{"class":1918},[138,17336,17338],{"id":17337},"_2-create-the-main-file","2. Create the Main File",[11,17340,17341],{},"Create our main TypeScript file:",[299,17343,17345],{"className":2665,"code":17344,"language":2667,"meta":307,"style":307},"touch main.ts\n",[179,17346,17347],{"__ignoreMap":307},[1736,17348,17349,17352],{"class":1738,"line":1739},[1736,17350,17351],{"class":2674},"touch",[1736,17353,17354],{"class":1935}," main.ts\n",[138,17356,17358],{"id":17357},"_3-configure-packagejson","3. Configure Package.json",[11,17360,17361,17362,17365,17366,17369],{},"Open the project in VS Code (or your preferred editor) and modify ",[179,17363,17364],{},"package.json"," to enable ES modules by adding the ",[179,17367,17368],{},"type"," field:",[299,17371,17373],{"className":1903,"code":17372,"language":1905,"meta":307,"style":307},"{\n  \"name\": \"mcp-weather-server\",\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  }\n}\n",[179,17374,17375,17379,17391,17403,17415,17422,17443,17447],{"__ignoreMap":307},[1736,17376,17377],{"class":1738,"line":1739},[1736,17378,1913],{"class":1912},[1736,17380,17381,17384,17386,17389],{"class":1738,"line":748},[1736,17382,17383],{"class":1918},"  \"name\"",[1736,17385,3065],{"class":1912},[1736,17387,17388],{"class":1935},"\"mcp-weather-server\"",[1736,17390,1939],{"class":1912},[1736,17392,17393,17396,17398,17401],{"class":1738,"line":756},[1736,17394,17395],{"class":1918},"  \"version\"",[1736,17397,3065],{"class":1912},[1736,17399,17400],{"class":1935},"\"1.0.0\"",[1736,17402,1939],{"class":1912},[1736,17404,17405,17408,17410,17413],{"class":1738,"line":1755},[1736,17406,17407],{"class":1918},"  \"type\"",[1736,17409,3065],{"class":1912},[1736,17411,17412],{"class":1935},"\"module\"",[1736,17414,1939],{"class":1912},[1736,17416,17417,17420],{"class":1738,"line":1761},[1736,17418,17419],{"class":1918},"  \"scripts\"",[1736,17421,1922],{"class":1912},[1736,17423,17424,17427,17429,17432,17435,17438,17440],{"class":1738,"line":1767},[1736,17425,17426],{"class":1918},"    \"test\"",[1736,17428,3065],{"class":1912},[1736,17430,17431],{"class":1935},"\"echo ",[1736,17433,17434],{"class":1918},"\\\"",[1736,17436,17437],{"class":1935},"Error: no test specified",[1736,17439,17434],{"class":1918},[1736,17441,17442],{"class":1935}," && exit 1\"\n",[1736,17444,17445],{"class":1738,"line":1772},[1736,17446,1971],{"class":1912},[1736,17448,17449],{"class":1738,"line":1778},[1736,17450,1976],{"class":1912},[1713,17452,17453],{},[11,17454,17455,17458],{},[58,17456,17457],{},"Why ES modules?"," The MCP SDK uses modern JavaScript modules, so we need to enable them in our project.",[23,17460,17462],{"id":17461},"step-2-install-dependencies","Step 2: Install Dependencies",[11,17464,17465],{},"Our MCP server needs two key libraries:",[138,17467,17469],{"id":17468},"_1-install-the-mcp-sdk","1. Install the MCP SDK",[11,17471,17472],{},"The Model Context Protocol SDK provides everything needed to build MCP servers:",[299,17474,17476],{"className":2665,"code":17475,"language":2667,"meta":307,"style":307},"npm install @modelcontextprotocol/sdk\n",[179,17477,17478],{"__ignoreMap":307},[1736,17479,17480,17482,17484],{"class":1738,"line":1739},[1736,17481,6565],{"class":2674},[1736,17483,4973],{"class":1935},[1736,17485,15528],{"class":1935},[138,17487,17489],{"id":17488},"_2-install-zod-for-data-validation","2. Install Zod for Data Validation",[11,17491,17492],{},"Zod ensures our server receives valid data from AI agents:",[299,17494,17496],{"className":2665,"code":17495,"language":2667,"meta":307,"style":307},"npm install zod\n",[179,17497,17498],{"__ignoreMap":307},[1736,17499,17500,17502,17504],{"class":1738,"line":1739},[1736,17501,6565],{"class":2674},[1736,17503,4973],{"class":1935},[1736,17505,17506],{"class":1935}," zod\n",[11,17508,17509,17510,17512],{},"Your ",[179,17511,17364],{}," dependencies should now look like this:",[299,17514,17516],{"className":1903,"code":17515,"language":1905,"meta":307,"style":307},"\"dependencies\": {\n  \"@modelcontextprotocol/sdk\": \"^1.13.1\",\n  \"zod\": \"^3.25.67\"\n}\n",[179,17517,17518,17525,17537,17547],{"__ignoreMap":307},[1736,17519,17520,17523],{"class":1738,"line":1739},[1736,17521,17522],{"class":1935},"\"dependencies\"",[1736,17524,1922],{"class":1912},[1736,17526,17527,17530,17532,17535],{"class":1738,"line":748},[1736,17528,17529],{"class":1918},"  \"@modelcontextprotocol/sdk\"",[1736,17531,3065],{"class":1912},[1736,17533,17534],{"class":1935},"\"^1.13.1\"",[1736,17536,1939],{"class":1912},[1736,17538,17539,17542,17544],{"class":1738,"line":756},[1736,17540,17541],{"class":1918},"  \"zod\"",[1736,17543,3065],{"class":1912},[1736,17545,17546],{"class":1935},"\"^3.25.67\"\n",[1736,17548,17549],{"class":1738,"line":1755},[1736,17550,1976],{"class":1912},[23,17552,17554],{"id":17553},"step-3-building-the-basic-server","Step 3: Building the Basic Server",[11,17556,17557,17558,17561],{},"Now let's create our MCP server. Open ",[179,17559,17560],{},"main.ts"," and let's build it step by step.",[138,17563,17565],{"id":17564},"_1-add-the-required-imports","1. Add the Required Imports",[299,17567,17569],{"className":15557,"code":17568,"language":15559,"meta":307,"style":307},"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from \"zod\";\n",[179,17570,17571,17585,17597],{"__ignoreMap":307},[1736,17572,17573,17575,17578,17580,17583],{"class":1738,"line":1739},[1736,17574,4996],{"class":4866},[1736,17576,17577],{"class":1912}," { McpServer } ",[1736,17579,5002],{"class":4866},[1736,17581,17582],{"class":1935}," \"@modelcontextprotocol/sdk/server/mcp.js\"",[1736,17584,7682],{"class":1912},[1736,17586,17587,17589,17591,17593,17595],{"class":1738,"line":748},[1736,17588,4996],{"class":4866},[1736,17590,15582],{"class":1912},[1736,17592,5002],{"class":4866},[1736,17594,15587],{"class":1935},[1736,17596,7682],{"class":1912},[1736,17598,17599,17601,17604,17606,17609],{"class":1738,"line":756},[1736,17600,4996],{"class":4866},[1736,17602,17603],{"class":1912}," { z } ",[1736,17605,5002],{"class":4866},[1736,17607,17608],{"class":1935}," \"zod\"",[1736,17610,7682],{"class":1912},[138,17612,17614],{"id":17613},"_2-create-the-server-instance","2. Create the Server Instance",[299,17616,17618],{"className":15557,"code":17617,"language":15559,"meta":307,"style":307},"const server = new McpServer({\n  name: \"Weather Server\",\n  version: \"1.0.0\"\n});\n",[179,17619,17620,17635,17645,17653],{"__ignoreMap":307},[1736,17621,17622,17624,17626,17628,17630,17633],{"class":1738,"line":1739},[1736,17623,5029],{"class":4866},[1736,17625,15643],{"class":1918},[1736,17627,4911],{"class":4866},[1736,17629,15674],{"class":4866},[1736,17631,17632],{"class":2674}," McpServer",[1736,17634,5122],{"class":1912},[1736,17636,17637,17640,17643],{"class":1738,"line":748},[1736,17638,17639],{"class":1912},"  name: ",[1736,17641,17642],{"class":1935},"\"Weather Server\"",[1736,17644,1939],{"class":1912},[1736,17646,17647,17650],{"class":1738,"line":756},[1736,17648,17649],{"class":1912},"  version: ",[1736,17651,17652],{"class":1935},"\"1.0.0\"\n",[1736,17654,17655],{"class":1738,"line":1755},[1736,17656,17657],{"class":1912},"});\n",[11,17659,17660],{},"The server manages all communication using the MCP protocol between clients (like VS Code) and your tools.",[138,17662,17664],{"id":17663},"_3-define-your-first-tool","3. Define Your First Tool",[11,17666,17667,17668,17671],{},"Tools are functions that AI agents can call. Let's create a ",[179,17669,17670],{},"get-weather"," tool:",[299,17673,17675],{"className":15557,"code":17674,"language":15559,"meta":307,"style":307},"server.tool(\n  'get-weather',\n  'Tool to get the weather of a city',\n  {\n    city: z.string().describe(\"The name of the city to get the weather for\")\n  },\n  async ({ city }) => {\n    // For now, return a simple static response\n    return {\n      content: [{\n        type: \"text\",\n        text: `The weather in ${city} is sunny`\n      }]\n    };\n  }\n);\n",[179,17676,17677,17686,17693,17700,17705,17725,17729,17745,17750,17756,17761,17770,17782,17787,17792,17796],{"__ignoreMap":307},[1736,17678,17679,17681,17684],{"class":1738,"line":1739},[1736,17680,16215],{"class":1912},[1736,17682,17683],{"class":2674},"tool",[1736,17685,14949],{"class":1912},[1736,17687,17688,17691],{"class":1738,"line":748},[1736,17689,17690],{"class":1935},"  'get-weather'",[1736,17692,1939],{"class":1912},[1736,17694,17695,17698],{"class":1738,"line":756},[1736,17696,17697],{"class":1935},"  'Tool to get the weather of a city'",[1736,17699,1939],{"class":1912},[1736,17701,17702],{"class":1738,"line":1755},[1736,17703,17704],{"class":1912},"  {\n",[1736,17706,17707,17710,17713,17715,17718,17720,17723],{"class":1738,"line":1761},[1736,17708,17709],{"class":1912},"    city: z.",[1736,17711,17712],{"class":2674},"string",[1736,17714,16221],{"class":1912},[1736,17716,17717],{"class":2674},"describe",[1736,17719,7751],{"class":1912},[1736,17721,17722],{"class":1935},"\"The name of the city to get the weather for\"",[1736,17724,7045],{"class":1912},[1736,17726,17727],{"class":1738,"line":1767},[1736,17728,4929],{"class":1912},[1736,17730,17731,17733,17736,17739,17741,17743],{"class":1738,"line":1772},[1736,17732,16127],{"class":4866},[1736,17734,17735],{"class":1912}," ({ ",[1736,17737,17738],{"class":5036},"city",[1736,17740,7778],{"class":1912},[1736,17742,7013],{"class":4866},[1736,17744,4914],{"class":1912},[1736,17746,17747],{"class":1738,"line":1778},[1736,17748,17749],{"class":6820},"    // For now, return a simple static response\n",[1736,17751,17752,17754],{"class":1738,"line":1784},[1736,17753,14242],{"class":4866},[1736,17755,4914],{"class":1912},[1736,17757,17758],{"class":1738,"line":1790},[1736,17759,17760],{"class":1912},"      content: [{\n",[1736,17762,17763,17765,17768],{"class":1738,"line":1796},[1736,17764,16613],{"class":1912},[1736,17766,17767],{"class":1935},"\"text\"",[1736,17769,1939],{"class":1912},[1736,17771,17772,17774,17777,17779],{"class":1738,"line":2353},[1736,17773,16622],{"class":1912},[1736,17775,17776],{"class":1935},"`The weather in ${",[1736,17778,17738],{"class":1912},[1736,17780,17781],{"class":1935},"} is sunny`\n",[1736,17783,17784],{"class":1738,"line":2358},[1736,17785,17786],{"class":1912},"      }]\n",[1736,17788,17789],{"class":1738,"line":2364},[1736,17790,17791],{"class":1912},"    };\n",[1736,17793,17794],{"class":1738,"line":2370},[1736,17795,1971],{"class":1912},[1736,17797,17798],{"class":1738,"line":2376},[1736,17799,16106],{"class":1912},[11,17801,17802],{},[58,17803,17804],{},"Breaking down the tool definition:",[70,17806,17807,17816,17822,17828],{},[73,17808,17809,17013,17812,17815],{},[58,17810,17811],{},"Tool ID:",[179,17813,17814],{},"'get-weather'"," - Unique identifier",[73,17817,17818,17821],{},[58,17819,17820],{},"Description:"," Helps AI agents understand what this tool does",[73,17823,17824,17827],{},[58,17825,17826],{},"Schema:"," Defines parameters (city must be a string)",[73,17829,17830,17833],{},[58,17831,17832],{},"Function:"," The actual code that runs when called",[11,17835,17836],{},[58,17837,17838],{},"How it works:",[70,17840,17841,17844,17850,17853],{},[73,17842,17843],{},"AI agent sees: \"Tool to get the weather of a city\"",[73,17845,17846,17847],{},"AI agent calls it with: ",[179,17848,17849],{},"{ city: \"Paris\" }",[73,17851,17852],{},"Zod validates the input",[73,17854,17855],{},"Function returns: \"The weather in Paris is sunny\"",[138,17857,17859],{"id":17858},"_4-set-up-communication","4. Set Up Communication",[11,17861,17862],{},"Finally, we need to set up how our server communicates with AI clients:",[299,17864,17866],{"className":15557,"code":17865,"language":15559,"meta":307,"style":307},"const transport = new StdioServerTransport();\nserver.connect(transport);\n",[179,17867,17868,17882],{"__ignoreMap":307},[1736,17869,17870,17872,17874,17876,17878,17880],{"class":1738,"line":1739},[1736,17871,5029],{"class":4866},[1736,17873,16139],{"class":1918},[1736,17875,4911],{"class":4866},[1736,17877,15674],{"class":4866},[1736,17879,16146],{"class":2674},[1736,17881,15752],{"class":1912},[1736,17883,17884,17886,17888],{"class":1738,"line":748},[1736,17885,16215],{"class":1912},[1736,17887,16161],{"class":2674},[1736,17889,16164],{"class":1912},[11,17891,17892],{},[58,17893,17894],{},"What's happening here:",[70,17896,17897,17903,17906,17916],{},[73,17898,17899,17902],{},[179,17900,17901],{},"StdioServerTransport"," uses your terminal's input/output for communication",[73,17904,17905],{},"Perfect for local development",[73,17907,17908,17909,17912,17913],{},"The server reads requests from ",[179,17910,17911],{},"stdin"," and writes responses to ",[179,17914,17915],{},"stdout",[73,17917,17918],{},"MCP protocol handles all the message formatting automatically",[138,17920,17922],{"id":17921},"_5-complete-basic-server-example","5. Complete Basic Server Example",[11,17924,17925,17926,17928],{},"Your complete ",[179,17927,17560],{}," should now look like this:",[299,17930,17932],{"className":15557,"code":17931,"language":15559,"meta":307,"style":307},"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from \"zod\";\n\nconst server = new McpServer({\n  name: \"Weather Server\",\n  version: \"1.0.0\"\n});\n\nserver.tool(\n  'get-weather',\n  'Tool to get the weather of a city',\n  {\n    city: z.string().describe(\"The name of the city to get the weather for\")\n  },\n  async ({ city }) => {\n    return {\n      content: [{\n        type: \"text\",\n        text: `The weather in ${city} is sunny`\n      }]\n    };\n  }\n);\n\nconst transport = new StdioServerTransport();\nserver.connect(transport);\n",[179,17933,17934,17946,17958,17970,17974,17988,17996,18002,18006,18010,18018,18024,18030,18034,18050,18054,18068,18074,18078,18086,18096,18100,18104,18108,18112,18116,18130],{"__ignoreMap":307},[1736,17935,17936,17938,17940,17942,17944],{"class":1738,"line":1739},[1736,17937,4996],{"class":4866},[1736,17939,17577],{"class":1912},[1736,17941,5002],{"class":4866},[1736,17943,17582],{"class":1935},[1736,17945,7682],{"class":1912},[1736,17947,17948,17950,17952,17954,17956],{"class":1738,"line":748},[1736,17949,4996],{"class":4866},[1736,17951,15582],{"class":1912},[1736,17953,5002],{"class":4866},[1736,17955,15587],{"class":1935},[1736,17957,7682],{"class":1912},[1736,17959,17960,17962,17964,17966,17968],{"class":1738,"line":756},[1736,17961,4996],{"class":4866},[1736,17963,17603],{"class":1912},[1736,17965,5002],{"class":4866},[1736,17967,17608],{"class":1935},[1736,17969,7682],{"class":1912},[1736,17971,17972],{"class":1738,"line":1755},[1736,17973,1747],{"emptyLinePlaceholder":790},[1736,17975,17976,17978,17980,17982,17984,17986],{"class":1738,"line":1761},[1736,17977,5029],{"class":4866},[1736,17979,15643],{"class":1918},[1736,17981,4911],{"class":4866},[1736,17983,15674],{"class":4866},[1736,17985,17632],{"class":2674},[1736,17987,5122],{"class":1912},[1736,17989,17990,17992,17994],{"class":1738,"line":1767},[1736,17991,17639],{"class":1912},[1736,17993,17642],{"class":1935},[1736,17995,1939],{"class":1912},[1736,17997,17998,18000],{"class":1738,"line":1772},[1736,17999,17649],{"class":1912},[1736,18001,17652],{"class":1935},[1736,18003,18004],{"class":1738,"line":1778},[1736,18005,17657],{"class":1912},[1736,18007,18008],{"class":1738,"line":1784},[1736,18009,1747],{"emptyLinePlaceholder":790},[1736,18011,18012,18014,18016],{"class":1738,"line":1790},[1736,18013,16215],{"class":1912},[1736,18015,17683],{"class":2674},[1736,18017,14949],{"class":1912},[1736,18019,18020,18022],{"class":1738,"line":1796},[1736,18021,17690],{"class":1935},[1736,18023,1939],{"class":1912},[1736,18025,18026,18028],{"class":1738,"line":2353},[1736,18027,17697],{"class":1935},[1736,18029,1939],{"class":1912},[1736,18031,18032],{"class":1738,"line":2358},[1736,18033,17704],{"class":1912},[1736,18035,18036,18038,18040,18042,18044,18046,18048],{"class":1738,"line":2364},[1736,18037,17709],{"class":1912},[1736,18039,17712],{"class":2674},[1736,18041,16221],{"class":1912},[1736,18043,17717],{"class":2674},[1736,18045,7751],{"class":1912},[1736,18047,17722],{"class":1935},[1736,18049,7045],{"class":1912},[1736,18051,18052],{"class":1738,"line":2370},[1736,18053,4929],{"class":1912},[1736,18055,18056,18058,18060,18062,18064,18066],{"class":1738,"line":2376},[1736,18057,16127],{"class":4866},[1736,18059,17735],{"class":1912},[1736,18061,17738],{"class":5036},[1736,18063,7778],{"class":1912},[1736,18065,7013],{"class":4866},[1736,18067,4914],{"class":1912},[1736,18069,18070,18072],{"class":1738,"line":2381},[1736,18071,14242],{"class":4866},[1736,18073,4914],{"class":1912},[1736,18075,18076],{"class":1738,"line":2387},[1736,18077,17760],{"class":1912},[1736,18079,18080,18082,18084],{"class":1738,"line":2393},[1736,18081,16613],{"class":1912},[1736,18083,17767],{"class":1935},[1736,18085,1939],{"class":1912},[1736,18087,18088,18090,18092,18094],{"class":1738,"line":2398},[1736,18089,16622],{"class":1912},[1736,18091,17776],{"class":1935},[1736,18093,17738],{"class":1912},[1736,18095,17781],{"class":1935},[1736,18097,18098],{"class":1738,"line":2404},[1736,18099,17786],{"class":1912},[1736,18101,18102],{"class":1738,"line":6959},[1736,18103,17791],{"class":1912},[1736,18105,18106],{"class":1738,"line":7296},[1736,18107,1971],{"class":1912},[1736,18109,18110],{"class":1738,"line":7305},[1736,18111,16106],{"class":1912},[1736,18113,18114],{"class":1738,"line":7310},[1736,18115,1747],{"emptyLinePlaceholder":790},[1736,18117,18118,18120,18122,18124,18126,18128],{"class":1738,"line":9659},[1736,18119,5029],{"class":4866},[1736,18121,16139],{"class":1918},[1736,18123,4911],{"class":4866},[1736,18125,15674],{"class":4866},[1736,18127,16146],{"class":2674},[1736,18129,15752],{"class":1912},[1736,18131,18132,18134,18136],{"class":1738,"line":9680},[1736,18133,16215],{"class":1912},[1736,18135,16161],{"class":2674},[1736,18137,16164],{"class":1912},[11,18139,18140,18141,18144],{},"🎉 ",[58,18142,18143],{},"Congratulations!"," You've built your first MCP server. Let's test it!",[23,18146,18148],{"id":18147},"step-4-testing-with-mcp-inspector","Step 4: Testing with MCP Inspector",[11,18150,18151],{},"Before adding real weather data, let's test our server using the MCP Inspector, a web-based debugging tool for MCP servers.",[138,18153,18155],{"id":18154},"launch-the-inspector","Launch the Inspector",[11,18157,18158],{},"Run this command to open the MCP Inspector for your server:",[299,18160,18162],{"className":2665,"code":18161,"language":2667,"meta":307,"style":307},"npx -y @modelcontextprotocol/inspector npx -y tsx main.ts\n",[179,18163,18164],{"__ignoreMap":307},[1736,18165,18166,18168,18171,18174,18177,18179,18182],{"class":1738,"line":1739},[1736,18167,2675],{"class":2674},[1736,18169,18170],{"class":1918}," -y",[1736,18172,18173],{"class":1935}," @modelcontextprotocol/inspector",[1736,18175,18176],{"class":1935}," npx",[1736,18178,18170],{"class":1918},[1736,18180,18181],{"class":1935}," tsx",[1736,18183,17354],{"class":1935},[11,18185,18186],{},"After running the command, you'll see terminal output with:",[70,18188,18189,18195,18198],{},[73,18190,18191,18192,952],{},"A localhost URL (like ",[179,18193,18194],{},"http://127.0.0.1:6274",[73,18196,18197],{},"A unique session token",[73,18199,18200],{},"A direct link with the token pre-filled",[11,18202,18203,18206],{},[58,18204,18205],{},"💡 Tip:"," Click the link with the token already included to avoid manual entry.",[138,18208,18210],{"id":18209},"connect-and-test","Connect and Test",[2260,18212,18213,18219,18225,18234],{},[73,18214,18215,18218],{},[58,18216,18217],{},"Connect:"," Click the \"Connect\" button in the Inspector",[73,18220,18221,18224],{},[58,18222,18223],{},"Navigate:"," Click \"Tools\" in the top navigation",[73,18226,18227,18230,18231,18233],{},[58,18228,18229],{},"Select:"," Choose your ",[179,18232,17670],{}," tool",[73,18235,18236,18239],{},[58,18237,18238],{},"Test:"," Enter a city name (like \"Palma de Mallorca\") and click \"Run Tool\"",[11,18241,18242,18243],{},"You should see the response: ",[179,18244,18245],{},"\"The weather in Palma de Mallorca is sunny\"",[11,18247,18248],{},[58,18249,18250],{},"Troubleshooting:",[70,18252,18253],{},[73,18254,18255,18258],{},[58,18256,18257],{},"Connection Error?"," Make sure you used the link with the pre-filled token",[11,18260,18261],{},"Perfect! Your MCP server is working. Now let's make it actually useful.",[23,18263,18265],{"id":18264},"step-5-adding-real-weather-data","Step 5: Adding Real Weather Data",[11,18267,18268,18269,18274],{},"Time to make our server actually useful! We'll integrate with ",[15,18270,18273],{"href":18271,"rel":18272},"https://open-meteo.com/",[19],"Open-Meteo",", a free weather API that requires no API key.",[138,18276,18278],{"id":18277},"how-the-weather-api-works","How the Weather API Works",[11,18280,18281],{},"To get weather data, we need a two-step process:",[2260,18283,18284,18290],{},[73,18285,18286,18289],{},[58,18287,18288],{},"Convert city name → coordinates"," (using the Geocoding API)",[73,18291,18292,18295],{},[58,18293,18294],{},"Get weather using coordinates"," (using the Weather API)",[138,18297,18299],{"id":18298},"update-your-tool-function","Update Your Tool Function",[11,18301,18302],{},"Replace your existing tool function with this enhanced version:",[299,18304,18306],{"className":15557,"code":18305,"language":15559,"meta":307,"style":307},"server.tool(\n  'get-weather',\n  'Tool to get the weather of a city',\n  {\n    city: z.string().describe(\"The name of the city to get the weather for\")\n  },\n  async ({ city }) => {\n    try {\n      // Step 1: Get coordinates for the city\n      const geoResponse = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`);\n      const geoData = await geoResponse.json();\n\n      // Handle city not found\n      if (!geoData.results || geoData.results.length === 0) {\n        return {\n          content: [{\n            type: \"text\",\n            text: `Sorry, I couldn't find a city named \"${city}\". Please check the spelling and try again.`\n          }]\n        };\n      }\n\n      // Step 2: Get weather data using coordinates\n      const { latitude, longitude } = geoData.results[0];\n      const weatherResponse = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1`);\n      const weatherData = await weatherResponse.json();\n\n      // Return the complete weather data as JSON\n      return {\n        content: [{\n          type: \"text\",\n          text: JSON.stringify(weatherData, null, 2)\n        }]\n      };\n    } catch (error) {\n      return {\n        content: [{\n          type: \"text\",\n          text: `Error fetching weather data: ${error.message}`\n        }]\n      };\n    }\n  }\n);\n",[179,18307,18308,18316,18322,18328,18332,18348,18352,18366,18373,18378,18405,18423,18427,18432,18458,18464,18469,18478,18491,18496,18501,18505,18509,18514,18539,18569,18587,18591,18596,18602,18607,18616,18641,18646,18650,18659,18665,18669,18677,18694,18698,18702,18706,18710],{"__ignoreMap":307},[1736,18309,18310,18312,18314],{"class":1738,"line":1739},[1736,18311,16215],{"class":1912},[1736,18313,17683],{"class":2674},[1736,18315,14949],{"class":1912},[1736,18317,18318,18320],{"class":1738,"line":748},[1736,18319,17690],{"class":1935},[1736,18321,1939],{"class":1912},[1736,18323,18324,18326],{"class":1738,"line":756},[1736,18325,17697],{"class":1935},[1736,18327,1939],{"class":1912},[1736,18329,18330],{"class":1738,"line":1755},[1736,18331,17704],{"class":1912},[1736,18333,18334,18336,18338,18340,18342,18344,18346],{"class":1738,"line":1761},[1736,18335,17709],{"class":1912},[1736,18337,17712],{"class":2674},[1736,18339,16221],{"class":1912},[1736,18341,17717],{"class":2674},[1736,18343,7751],{"class":1912},[1736,18345,17722],{"class":1935},[1736,18347,7045],{"class":1912},[1736,18349,18350],{"class":1738,"line":1767},[1736,18351,4929],{"class":1912},[1736,18353,18354,18356,18358,18360,18362,18364],{"class":1738,"line":1772},[1736,18355,16127],{"class":4866},[1736,18357,17735],{"class":1912},[1736,18359,17738],{"class":5036},[1736,18361,7778],{"class":1912},[1736,18363,7013],{"class":4866},[1736,18365,4914],{"class":1912},[1736,18367,18368,18371],{"class":1738,"line":1778},[1736,18369,18370],{"class":4866},"    try",[1736,18372,4914],{"class":1912},[1736,18374,18375],{"class":1738,"line":1784},[1736,18376,18377],{"class":6820},"      // Step 1: Get coordinates for the city\n",[1736,18379,18380,18382,18385,18387,18390,18393,18395,18398,18400,18403],{"class":1738,"line":1790},[1736,18381,14312],{"class":4866},[1736,18383,18384],{"class":1918}," geoResponse",[1736,18386,4911],{"class":4866},[1736,18388,18389],{"class":4866}," await",[1736,18391,18392],{"class":2674}," fetch",[1736,18394,7751],{"class":1912},[1736,18396,18397],{"class":1935},"`https://geocoding-api.open-meteo.com/v1/search?name=${",[1736,18399,17738],{"class":1912},[1736,18401,18402],{"class":1935},"}&count=1&language=en&format=json`",[1736,18404,16106],{"class":1912},[1736,18406,18407,18409,18412,18414,18416,18419,18421],{"class":1738,"line":1796},[1736,18408,14312],{"class":4866},[1736,18410,18411],{"class":1918}," geoData",[1736,18413,4911],{"class":4866},[1736,18415,18389],{"class":4866},[1736,18417,18418],{"class":1912}," geoResponse.",[1736,18420,1905],{"class":2674},[1736,18422,15752],{"class":1912},[1736,18424,18425],{"class":1738,"line":2353},[1736,18426,1747],{"emptyLinePlaceholder":790},[1736,18428,18429],{"class":1738,"line":2358},[1736,18430,18431],{"class":6820},"      // Handle city not found\n",[1736,18433,18434,18436,18438,18440,18443,18445,18448,18451,18454,18456],{"class":1738,"line":2364},[1736,18435,14351],{"class":4866},[1736,18437,1095],{"class":1912},[1736,18439,1690],{"class":4866},[1736,18441,18442],{"class":1912},"geoData.results ",[1736,18444,7792],{"class":4866},[1736,18446,18447],{"class":1912}," geoData.results.",[1736,18449,18450],{"class":1918},"length",[1736,18452,18453],{"class":4866}," ===",[1736,18455,16869],{"class":1918},[1736,18457,7246],{"class":1912},[1736,18459,18460,18462],{"class":1738,"line":2370},[1736,18461,14749],{"class":4866},[1736,18463,4914],{"class":1912},[1736,18465,18466],{"class":1738,"line":2376},[1736,18467,18468],{"class":1912},"          content: [{\n",[1736,18470,18471,18474,18476],{"class":1738,"line":2381},[1736,18472,18473],{"class":1912},"            type: ",[1736,18475,17767],{"class":1935},[1736,18477,1939],{"class":1912},[1736,18479,18480,18483,18486,18488],{"class":1738,"line":2387},[1736,18481,18482],{"class":1912},"            text: ",[1736,18484,18485],{"class":1935},"`Sorry, I couldn't find a city named \"${",[1736,18487,17738],{"class":1912},[1736,18489,18490],{"class":1935},"}\". Please check the spelling and try again.`\n",[1736,18492,18493],{"class":1738,"line":2393},[1736,18494,18495],{"class":1912},"          }]\n",[1736,18497,18498],{"class":1738,"line":2398},[1736,18499,18500],{"class":1912},"        };\n",[1736,18502,18503],{"class":1738,"line":2404},[1736,18504,14448],{"class":1912},[1736,18506,18507],{"class":1738,"line":6959},[1736,18508,1747],{"emptyLinePlaceholder":790},[1736,18510,18511],{"class":1738,"line":7296},[1736,18512,18513],{"class":6820},"      // Step 2: Get weather data using coordinates\n",[1736,18515,18516,18518,18520,18523,18525,18528,18530,18532,18535,18537],{"class":1738,"line":7305},[1736,18517,14312],{"class":4866},[1736,18519,7827],{"class":1912},[1736,18521,18522],{"class":1918},"latitude",[1736,18524,829],{"class":1912},[1736,18526,18527],{"class":1918},"longitude",[1736,18529,7832],{"class":1912},[1736,18531,5062],{"class":4866},[1736,18533,18534],{"class":1912}," geoData.results[",[1736,18536,1290],{"class":1918},[1736,18538,7800],{"class":1912},[1736,18540,18541,18543,18546,18548,18550,18552,18554,18557,18559,18562,18564,18567],{"class":1738,"line":7310},[1736,18542,14312],{"class":4866},[1736,18544,18545],{"class":1918}," weatherResponse",[1736,18547,4911],{"class":4866},[1736,18549,18389],{"class":4866},[1736,18551,18392],{"class":2674},[1736,18553,7751],{"class":1912},[1736,18555,18556],{"class":1935},"`https://api.open-meteo.com/v1/forecast?latitude=${",[1736,18558,18522],{"class":1912},[1736,18560,18561],{"class":1935},"}&longitude=${",[1736,18563,18527],{"class":1912},[1736,18565,18566],{"class":1935},"}&current=temperature_2m,relative_humidity_2m,apparent_temperature,precipitation,weather_code&hourly=temperature_2m,precipitation&forecast_days=1`",[1736,18568,16106],{"class":1912},[1736,18570,18571,18573,18576,18578,18580,18583,18585],{"class":1738,"line":9659},[1736,18572,14312],{"class":4866},[1736,18574,18575],{"class":1918}," weatherData",[1736,18577,4911],{"class":4866},[1736,18579,18389],{"class":4866},[1736,18581,18582],{"class":1912}," weatherResponse.",[1736,18584,1905],{"class":2674},[1736,18586,15752],{"class":1912},[1736,18588,18589],{"class":1738,"line":9680},[1736,18590,1747],{"emptyLinePlaceholder":790},[1736,18592,18593],{"class":1738,"line":9699},[1736,18594,18595],{"class":6820},"      // Return the complete weather data as JSON\n",[1736,18597,18598,18600],{"class":1738,"line":9704},[1736,18599,15801],{"class":4866},[1736,18601,4914],{"class":1912},[1736,18603,18604],{"class":1738,"line":9715},[1736,18605,18606],{"class":1912},"        content: [{\n",[1736,18608,18609,18612,18614],{"class":1738,"line":9727},[1736,18610,18611],{"class":1912},"          type: ",[1736,18613,17767],{"class":1935},[1736,18615,1939],{"class":1912},[1736,18617,18618,18621,18624,18626,18629,18632,18635,18637,18639],{"class":1738,"line":9739},[1736,18619,18620],{"class":1912},"          text: ",[1736,18622,18623],{"class":1918},"JSON",[1736,18625,891],{"class":1912},[1736,18627,18628],{"class":2674},"stringify",[1736,18630,18631],{"class":1912},"(weatherData, ",[1736,18633,18634],{"class":1918},"null",[1736,18636,829],{"class":1912},[1736,18638,10820],{"class":1918},[1736,18640,7045],{"class":1912},[1736,18642,18643],{"class":1738,"line":9750},[1736,18644,18645],{"class":1912},"        }]\n",[1736,18647,18648],{"class":1738,"line":9761},[1736,18649,15908],{"class":1912},[1736,18651,18652,18654,18656],{"class":1738,"line":9767},[1736,18653,5059],{"class":1912},[1736,18655,16224],{"class":4866},[1736,18657,18658],{"class":1912}," (error) {\n",[1736,18660,18661,18663],{"class":1738,"line":9778},[1736,18662,15801],{"class":4866},[1736,18664,4914],{"class":1912},[1736,18666,18667],{"class":1738,"line":9799},[1736,18668,18606],{"class":1912},[1736,18670,18671,18673,18675],{"class":1738,"line":9804},[1736,18672,18611],{"class":1912},[1736,18674,17767],{"class":1935},[1736,18676,1939],{"class":1912},[1736,18678,18679,18681,18684,18686,18688,18691],{"class":1738,"line":9814},[1736,18680,18620],{"class":1912},[1736,18682,18683],{"class":1935},"`Error fetching weather data: ${",[1736,18685,16172],{"class":1912},[1736,18687,891],{"class":1935},[1736,18689,18690],{"class":1912},"message",[1736,18692,18693],{"class":1935},"}`\n",[1736,18695,18696],{"class":1738,"line":9826},[1736,18697,18645],{"class":1912},[1736,18699,18700],{"class":1738,"line":9838},[1736,18701,15908],{"class":1912},[1736,18703,18704],{"class":1738,"line":9850},[1736,18705,9853],{"class":1912},[1736,18707,18708],{"class":1738,"line":9856},[1736,18709,1971],{"class":1912},[1736,18711,18712],{"class":1738,"line":9861},[1736,18713,16106],{"class":1912},[138,18715,18717],{"id":18716},"test-the-real-data","Test the Real Data",[2260,18719,18720,18726,18732],{},[73,18721,18722,18725],{},[58,18723,18724],{},"Restart"," your MCP Inspector (Ctrl+C, then re-run the command)",[73,18727,18728,18731],{},[58,18729,18730],{},"Reconnect"," in the web interface",[73,18733,18734,18736],{},[58,18735,1007],{}," with a real city like \"Tokyo\" or \"New York\"",[11,18738,18739],{},"You should now see actual weather data instead of \"sunny\"! 🌤️",[23,18741,18743],{"id":18742},"step-6-integration-with-vs-code-and-github-copilot","Step 6: Integration with VS Code and GitHub Copilot",[11,18745,18746],{},"Now let's connect your weather server to VS Code so you can use it with GitHub Copilot!",[138,18748,18750],{"id":18749},"register-the-server","Register the Server",[2260,18752,18753,18761,18769,18775,18783,18791],{},[73,18754,18755,17013,18758],{},[58,18756,18757],{},"Open Command Palette:",[179,18759,18760],{},"Cmd/Ctrl + Shift + P",[73,18762,18763,17013,18766],{},[58,18764,18765],{},"Type:",[179,18767,18768],{},"MCP: Add Server",[73,18770,18771,18774],{},[58,18772,18773],{},"Choose:"," \"Local server using stdio\"",[73,18776,18777,17013,18780],{},[58,18778,18779],{},"Enter Command:",[179,18781,18782],{},"npx -y tsx main.ts",[73,18784,18785,17013,18788],{},[58,18786,18787],{},"Name:",[179,18789,18790],{},"my-weather-server",[73,18792,18793,18796],{},[58,18794,18795],{},"Setup Type:"," Local setup",[11,18798,18799,18800,18803],{},"This creates a ",[179,18801,18802],{},".vscode/mcp.json"," file in your project:",[299,18805,18807],{"className":1903,"code":18806,"language":1905,"meta":307,"style":307},"{\n  \"inputs\": [],\n  \"servers\": {\n    \"my-weather-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"tsx\",\n        \"/Users/your-username/path/to/main.ts\"\n      ]\n    }\n  }\n}\n",[179,18808,18809,18813,18821,18828,18835,18847,18858,18864,18871,18878,18883,18888,18892,18896],{"__ignoreMap":307},[1736,18810,18811],{"class":1738,"line":1739},[1736,18812,1913],{"class":1912},[1736,18814,18815,18818],{"class":1738,"line":748},[1736,18816,18817],{"class":1918},"  \"inputs\"",[1736,18819,18820],{"class":1912},": [],\n",[1736,18822,18823,18826],{"class":1738,"line":756},[1736,18824,18825],{"class":1918},"  \"servers\"",[1736,18827,1922],{"class":1912},[1736,18829,18830,18833],{"class":1738,"line":1755},[1736,18831,18832],{"class":1918},"    \"my-weather-server\"",[1736,18834,1922],{"class":1912},[1736,18836,18837,18840,18842,18845],{"class":1738,"line":1761},[1736,18838,18839],{"class":1918},"      \"type\"",[1736,18841,3065],{"class":1912},[1736,18843,18844],{"class":1935},"\"stdio\"",[1736,18846,1939],{"class":1912},[1736,18848,18849,18851,18853,18856],{"class":1738,"line":1767},[1736,18850,17050],{"class":1918},[1736,18852,3065],{"class":1912},[1736,18854,18855],{"class":1935},"\"npx\"",[1736,18857,1939],{"class":1912},[1736,18859,18860,18862],{"class":1738,"line":1772},[1736,18861,17062],{"class":1918},[1736,18863,1930],{"class":1912},[1736,18865,18866,18869],{"class":1738,"line":1778},[1736,18867,18868],{"class":1935},"        \"-y\"",[1736,18870,1939],{"class":1912},[1736,18872,18873,18876],{"class":1738,"line":1784},[1736,18874,18875],{"class":1935},"        \"tsx\"",[1736,18877,1939],{"class":1912},[1736,18879,18880],{"class":1738,"line":1790},[1736,18881,18882],{"class":1935},"        \"/Users/your-username/path/to/main.ts\"\n",[1736,18884,18885],{"class":1738,"line":1796},[1736,18886,18887],{"class":1912},"      ]\n",[1736,18889,18890],{"class":1738,"line":2353},[1736,18891,9853],{"class":1912},[1736,18893,18894],{"class":1738,"line":2358},[1736,18895,1971],{"class":1912},[1736,18897,18898],{"class":1738,"line":2364},[1736,18899,1976],{"class":1912},[138,18901,18903],{"id":18902},"start-and-test","Start and Test",[2260,18905,18906,18912,18918,18924],{},[73,18907,18908,18911],{},[58,18909,18910],{},"Start the server:"," Click the \"Start\" button next to your server name in the MCP panel",[73,18913,18914,18917],{},[58,18915,18916],{},"Verify:"," You should see \"Running\" status",[73,18919,18920,18923],{},[58,18921,18922],{},"Switch to Agent Mode:"," Click the Copilot sidebar → \"Agent Mode\"",[73,18925,18926,18929],{},[58,18927,18928],{},"Ask:"," \"What's the weather like in Tokyo?\"",[11,18931,18932],{},"GitHub Copilot will ask permission to use your weather tool, click \"Continue\" to proceed.",[11,18934,18935,18938],{},[58,18936,18937],{},"Expected Result:"," Instead of raw JSON, you'll get a beautifully formatted weather report like this:",[299,18940,18942],{"className":2294,"code":18941,"language":2296,"meta":307,"style":307},"> **Weather in Tokyo Today**\n> **Temperature:** 28°C (feels like 32°C)\n> **Humidity:** 75%\n> **Conditions:** Partly cloudy with light rain expected in the evening\n",[179,18943,18944,18949,18954,18959],{"__ignoreMap":307},[1736,18945,18946],{"class":1738,"line":1739},[1736,18947,18948],{},"> **Weather in Tokyo Today**\n",[1736,18950,18951],{"class":1738,"line":748},[1736,18952,18953],{},"> **Temperature:** 28°C (feels like 32°C)\n",[1736,18955,18956],{"class":1738,"line":756},[1736,18957,18958],{},"> **Humidity:** 75%\n",[1736,18960,18961],{"class":1738,"line":1755},[1736,18962,18963],{},"> **Conditions:** Partly cloudy with light rain expected in the evening\n",[11,18965,18966,18969],{},[58,18967,18968],{},"Perfect!"," The AI transforms your raw weather data into a beautiful, human-readable format automatically.",[23,18971,18973],{"id":18972},"why-this-is-powerful","Why This is Powerful",[11,18975,18976],{},"Your weather server demonstrates the true power of MCP:",[11,18978,18979],{},[58,18980,18981],{},"🤖 AI Does the Heavy Lifting",[70,18983,18984,18987],{},[73,18985,18986],{},"You provide raw data, AI creates beautiful presentations",[73,18988,18989],{},"No need to format responses, the AI handles user experience",[11,18991,18992],{},[58,18993,18994],{},"🔗 Universal Compatibility",[70,18996,18997,19000],{},[73,18998,18999],{},"Works with any MCP-compatible tool (VS Code, Claude, etc.)",[73,19001,19002],{},"Write once, use everywhere",[11,19004,19005],{},[58,19006,19007],{},"⚡ Real-time Integration",[70,19009,19010,19013],{},[73,19011,19012],{},"Always current data, no caching issues",[73,19014,19015],{},"Works seamlessly within your development environment",[11,19017,19018],{},[58,19019,19020],{},"📈 Easy to Extend",[70,19022,19023,19026],{},[73,19024,19025],{},"Add weather alerts, forecasts, or air quality data",[73,19027,19028],{},"Build additional tools in the same server",[23,19030,19032],{"id":19031},"complete-code-reference","Complete Code Reference",[11,19034,19035,19036,19038],{},"Here's your final ",[179,19037,17560],{}," file:",[299,19040,19042],{"className":15557,"code":19041,"language":15559,"meta":307,"style":307},"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from \"zod\";\n\nconst server = new McpServer({\n  name: \"Weather Server\",\n  version: \"1.0.0\"\n});\n\nserver.tool(\n  'get-weather',\n  'Tool to get the weather of a city',\n  {\n    city: z.string().describe(\"The name of the city to get the weather for\")\n  },\n  async ({ city }) => {\n    try {\n      const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=10&language=en&format=json`);\n      const data = await response.json();\n\n      if (data.results.length === 0) {\n        return {\n          content: [{\n            type: \"text\",\n            text: `No results found for city: ${city}`\n          }]\n        };\n      }\n\n      const { latitude, longitude } = data.results[0];\n      const weatherResponse = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m,precipitation,apparent_temperature,relative_humidity_2m&forecast_days=1`);\n      const weatherData = await weatherResponse.json();\n\n      return {\n        content: [{\n          type: \"text\",\n          text: JSON.stringify(weatherData, null, 2)\n        }]\n      };\n    } catch (error) {\n      return {\n        content: [{\n          type: \"text\",\n          text: `Error fetching weather data: ${error.message}`\n        }]\n      };\n    }\n  }\n);\n\nconst transport = new StdioServerTransport();\nserver.connect(transport);\n",[179,19043,19044,19056,19068,19080,19084,19098,19106,19112,19116,19120,19128,19134,19140,19144,19160,19164,19178,19184,19208,19226,19230,19245,19251,19255,19263,19274,19278,19282,19286,19290,19313,19340,19356,19360,19366,19370,19378,19398,19402,19406,19414,19420,19424,19432,19446,19450,19454,19458,19462,19466,19470,19484],{"__ignoreMap":307},[1736,19045,19046,19048,19050,19052,19054],{"class":1738,"line":1739},[1736,19047,4996],{"class":4866},[1736,19049,17577],{"class":1912},[1736,19051,5002],{"class":4866},[1736,19053,17582],{"class":1935},[1736,19055,7682],{"class":1912},[1736,19057,19058,19060,19062,19064,19066],{"class":1738,"line":748},[1736,19059,4996],{"class":4866},[1736,19061,15582],{"class":1912},[1736,19063,5002],{"class":4866},[1736,19065,15587],{"class":1935},[1736,19067,7682],{"class":1912},[1736,19069,19070,19072,19074,19076,19078],{"class":1738,"line":756},[1736,19071,4996],{"class":4866},[1736,19073,17603],{"class":1912},[1736,19075,5002],{"class":4866},[1736,19077,17608],{"class":1935},[1736,19079,7682],{"class":1912},[1736,19081,19082],{"class":1738,"line":1755},[1736,19083,1747],{"emptyLinePlaceholder":790},[1736,19085,19086,19088,19090,19092,19094,19096],{"class":1738,"line":1761},[1736,19087,5029],{"class":4866},[1736,19089,15643],{"class":1918},[1736,19091,4911],{"class":4866},[1736,19093,15674],{"class":4866},[1736,19095,17632],{"class":2674},[1736,19097,5122],{"class":1912},[1736,19099,19100,19102,19104],{"class":1738,"line":1767},[1736,19101,17639],{"class":1912},[1736,19103,17642],{"class":1935},[1736,19105,1939],{"class":1912},[1736,19107,19108,19110],{"class":1738,"line":1772},[1736,19109,17649],{"class":1912},[1736,19111,17652],{"class":1935},[1736,19113,19114],{"class":1738,"line":1778},[1736,19115,17657],{"class":1912},[1736,19117,19118],{"class":1738,"line":1784},[1736,19119,1747],{"emptyLinePlaceholder":790},[1736,19121,19122,19124,19126],{"class":1738,"line":1790},[1736,19123,16215],{"class":1912},[1736,19125,17683],{"class":2674},[1736,19127,14949],{"class":1912},[1736,19129,19130,19132],{"class":1738,"line":1796},[1736,19131,17690],{"class":1935},[1736,19133,1939],{"class":1912},[1736,19135,19136,19138],{"class":1738,"line":2353},[1736,19137,17697],{"class":1935},[1736,19139,1939],{"class":1912},[1736,19141,19142],{"class":1738,"line":2358},[1736,19143,17704],{"class":1912},[1736,19145,19146,19148,19150,19152,19154,19156,19158],{"class":1738,"line":2364},[1736,19147,17709],{"class":1912},[1736,19149,17712],{"class":2674},[1736,19151,16221],{"class":1912},[1736,19153,17717],{"class":2674},[1736,19155,7751],{"class":1912},[1736,19157,17722],{"class":1935},[1736,19159,7045],{"class":1912},[1736,19161,19162],{"class":1738,"line":2370},[1736,19163,4929],{"class":1912},[1736,19165,19166,19168,19170,19172,19174,19176],{"class":1738,"line":2376},[1736,19167,16127],{"class":4866},[1736,19169,17735],{"class":1912},[1736,19171,17738],{"class":5036},[1736,19173,7778],{"class":1912},[1736,19175,7013],{"class":4866},[1736,19177,4914],{"class":1912},[1736,19179,19180,19182],{"class":1738,"line":2381},[1736,19181,18370],{"class":4866},[1736,19183,4914],{"class":1912},[1736,19185,19186,19188,19191,19193,19195,19197,19199,19201,19203,19206],{"class":1738,"line":2387},[1736,19187,14312],{"class":4866},[1736,19189,19190],{"class":1918}," response",[1736,19192,4911],{"class":4866},[1736,19194,18389],{"class":4866},[1736,19196,18392],{"class":2674},[1736,19198,7751],{"class":1912},[1736,19200,18397],{"class":1935},[1736,19202,17738],{"class":1912},[1736,19204,19205],{"class":1935},"}&count=10&language=en&format=json`",[1736,19207,16106],{"class":1912},[1736,19209,19210,19212,19215,19217,19219,19222,19224],{"class":1738,"line":2393},[1736,19211,14312],{"class":4866},[1736,19213,19214],{"class":1918}," data",[1736,19216,4911],{"class":4866},[1736,19218,18389],{"class":4866},[1736,19220,19221],{"class":1912}," response.",[1736,19223,1905],{"class":2674},[1736,19225,15752],{"class":1912},[1736,19227,19228],{"class":1738,"line":2398},[1736,19229,1747],{"emptyLinePlaceholder":790},[1736,19231,19232,19234,19237,19239,19241,19243],{"class":1738,"line":2404},[1736,19233,14351],{"class":4866},[1736,19235,19236],{"class":1912}," (data.results.",[1736,19238,18450],{"class":1918},[1736,19240,18453],{"class":4866},[1736,19242,16869],{"class":1918},[1736,19244,7246],{"class":1912},[1736,19246,19247,19249],{"class":1738,"line":6959},[1736,19248,14749],{"class":4866},[1736,19250,4914],{"class":1912},[1736,19252,19253],{"class":1738,"line":7296},[1736,19254,18468],{"class":1912},[1736,19256,19257,19259,19261],{"class":1738,"line":7305},[1736,19258,18473],{"class":1912},[1736,19260,17767],{"class":1935},[1736,19262,1939],{"class":1912},[1736,19264,19265,19267,19270,19272],{"class":1738,"line":7310},[1736,19266,18482],{"class":1912},[1736,19268,19269],{"class":1935},"`No results found for city: ${",[1736,19271,17738],{"class":1912},[1736,19273,18693],{"class":1935},[1736,19275,19276],{"class":1738,"line":9659},[1736,19277,18495],{"class":1912},[1736,19279,19280],{"class":1738,"line":9680},[1736,19281,18500],{"class":1912},[1736,19283,19284],{"class":1738,"line":9699},[1736,19285,14448],{"class":1912},[1736,19287,19288],{"class":1738,"line":9704},[1736,19289,1747],{"emptyLinePlaceholder":790},[1736,19291,19292,19294,19296,19298,19300,19302,19304,19306,19309,19311],{"class":1738,"line":9715},[1736,19293,14312],{"class":4866},[1736,19295,7827],{"class":1912},[1736,19297,18522],{"class":1918},[1736,19299,829],{"class":1912},[1736,19301,18527],{"class":1918},[1736,19303,7832],{"class":1912},[1736,19305,5062],{"class":4866},[1736,19307,19308],{"class":1912}," data.results[",[1736,19310,1290],{"class":1918},[1736,19312,7800],{"class":1912},[1736,19314,19315,19317,19319,19321,19323,19325,19327,19329,19331,19333,19335,19338],{"class":1738,"line":9727},[1736,19316,14312],{"class":4866},[1736,19318,18545],{"class":1918},[1736,19320,4911],{"class":4866},[1736,19322,18389],{"class":4866},[1736,19324,18392],{"class":2674},[1736,19326,7751],{"class":1912},[1736,19328,18556],{"class":1935},[1736,19330,18522],{"class":1912},[1736,19332,18561],{"class":1935},[1736,19334,18527],{"class":1912},[1736,19336,19337],{"class":1935},"}&hourly=temperature_2m,precipitation,apparent_temperature,relative_humidity_2m&forecast_days=1`",[1736,19339,16106],{"class":1912},[1736,19341,19342,19344,19346,19348,19350,19352,19354],{"class":1738,"line":9739},[1736,19343,14312],{"class":4866},[1736,19345,18575],{"class":1918},[1736,19347,4911],{"class":4866},[1736,19349,18389],{"class":4866},[1736,19351,18582],{"class":1912},[1736,19353,1905],{"class":2674},[1736,19355,15752],{"class":1912},[1736,19357,19358],{"class":1738,"line":9750},[1736,19359,1747],{"emptyLinePlaceholder":790},[1736,19361,19362,19364],{"class":1738,"line":9761},[1736,19363,15801],{"class":4866},[1736,19365,4914],{"class":1912},[1736,19367,19368],{"class":1738,"line":9767},[1736,19369,18606],{"class":1912},[1736,19371,19372,19374,19376],{"class":1738,"line":9778},[1736,19373,18611],{"class":1912},[1736,19375,17767],{"class":1935},[1736,19377,1939],{"class":1912},[1736,19379,19380,19382,19384,19386,19388,19390,19392,19394,19396],{"class":1738,"line":9799},[1736,19381,18620],{"class":1912},[1736,19383,18623],{"class":1918},[1736,19385,891],{"class":1912},[1736,19387,18628],{"class":2674},[1736,19389,18631],{"class":1912},[1736,19391,18634],{"class":1918},[1736,19393,829],{"class":1912},[1736,19395,10820],{"class":1918},[1736,19397,7045],{"class":1912},[1736,19399,19400],{"class":1738,"line":9804},[1736,19401,18645],{"class":1912},[1736,19403,19404],{"class":1738,"line":9814},[1736,19405,15908],{"class":1912},[1736,19407,19408,19410,19412],{"class":1738,"line":9826},[1736,19409,5059],{"class":1912},[1736,19411,16224],{"class":4866},[1736,19413,18658],{"class":1912},[1736,19415,19416,19418],{"class":1738,"line":9838},[1736,19417,15801],{"class":4866},[1736,19419,4914],{"class":1912},[1736,19421,19422],{"class":1738,"line":9850},[1736,19423,18606],{"class":1912},[1736,19425,19426,19428,19430],{"class":1738,"line":9856},[1736,19427,18611],{"class":1912},[1736,19429,17767],{"class":1935},[1736,19431,1939],{"class":1912},[1736,19433,19434,19436,19438,19440,19442,19444],{"class":1738,"line":9861},[1736,19435,18620],{"class":1912},[1736,19437,18683],{"class":1935},[1736,19439,16172],{"class":1912},[1736,19441,891],{"class":1935},[1736,19443,18690],{"class":1912},[1736,19445,18693],{"class":1935},[1736,19447,19448],{"class":1738,"line":9870},[1736,19449,18645],{"class":1912},[1736,19451,19452],{"class":1738,"line":9881},[1736,19453,15908],{"class":1912},[1736,19455,19456],{"class":1738,"line":9892},[1736,19457,9853],{"class":1912},[1736,19459,19460],{"class":1738,"line":9903},[1736,19461,1971],{"class":1912},[1736,19463,19464],{"class":1738,"line":9908},[1736,19465,16106],{"class":1912},[1736,19467,19468],{"class":1738,"line":9913},[1736,19469,1747],{"emptyLinePlaceholder":790},[1736,19471,19472,19474,19476,19478,19480,19482],{"class":1738,"line":9918},[1736,19473,5029],{"class":4866},[1736,19475,16139],{"class":1918},[1736,19477,4911],{"class":4866},[1736,19479,15674],{"class":4866},[1736,19481,16146],{"class":2674},[1736,19483,15752],{"class":1912},[1736,19485,19486,19488,19490],{"class":1738,"line":9931},[1736,19487,16215],{"class":1912},[1736,19489,16161],{"class":2674},[1736,19491,16164],{"class":1912},[23,19493,19495],{"id":19494},"next-steps-enhance-your-server","Next Steps: Enhance Your Server",[11,19497,19498],{},"Ready to take your weather server to the next level? Here are some ideas:",[138,19500,19502],{"id":19501},"additional-weather-tools","🚀 Additional Weather Tools",[11,19504,19505],{},[58,19506,19507],{},"Extended Forecasts",[299,19509,19511],{"className":15557,"code":19510,"language":15559,"meta":307,"style":307},"server.tool('get-forecast', 'Get 7-day weather forecast', ...)\n",[179,19512,19513],{"__ignoreMap":307},[1736,19514,19515,19517,19519,19521,19524,19526,19529,19531,19533],{"class":1738,"line":1739},[1736,19516,16215],{"class":1912},[1736,19518,17683],{"class":2674},[1736,19520,7751],{"class":1912},[1736,19522,19523],{"class":1935},"'get-forecast'",[1736,19525,829],{"class":1912},[1736,19527,19528],{"class":1935},"'Get 7-day weather forecast'",[1736,19530,829],{"class":1912},[1736,19532,11965],{"class":4866},[1736,19534,7045],{"class":1912},[11,19536,19537],{},[58,19538,19539],{},"Weather Alerts",[299,19541,19543],{"className":15557,"code":19542,"language":15559,"meta":307,"style":307},"server.tool('get-alerts', 'Get severe weather warnings', ...)\n",[179,19544,19545],{"__ignoreMap":307},[1736,19546,19547,19549,19551,19553,19556,19558,19561,19563,19565],{"class":1738,"line":1739},[1736,19548,16215],{"class":1912},[1736,19550,17683],{"class":2674},[1736,19552,7751],{"class":1912},[1736,19554,19555],{"class":1935},"'get-alerts'",[1736,19557,829],{"class":1912},[1736,19559,19560],{"class":1935},"'Get severe weather warnings'",[1736,19562,829],{"class":1912},[1736,19564,11965],{"class":4866},[1736,19566,7045],{"class":1912},[11,19568,19569],{},[58,19570,19571],{},"Air Quality",[299,19573,19575],{"className":15557,"code":19574,"language":15559,"meta":307,"style":307},"server.tool('get-air-quality', 'Get air pollution data', ...)\n",[179,19576,19577],{"__ignoreMap":307},[1736,19578,19579,19581,19583,19585,19588,19590,19593,19595,19597],{"class":1738,"line":1739},[1736,19580,16215],{"class":1912},[1736,19582,17683],{"class":2674},[1736,19584,7751],{"class":1912},[1736,19586,19587],{"class":1935},"'get-air-quality'",[1736,19589,829],{"class":1912},[1736,19591,19592],{"class":1935},"'Get air pollution data'",[1736,19594,829],{"class":1912},[1736,19596,11965],{"class":4866},[1736,19598,7045],{"class":1912},[138,19600,19602],{"id":19601},"sharing-your-server","📦 Sharing Your Server",[70,19604,19605],{},[73,19606,19607,19610],{},[58,19608,19609],{},"Publish to NPM:"," Make it available for others to use",[23,19612,3294],{"id":3293},[11,19614,18140,19615,19617],{},[58,19616,18143],{}," You've successfully built your first MCP weather server!",[11,19619,19620],{},[58,19621,19622],{},"What You've Accomplished:",[70,19624,19625,19628,19631,19634],{},[73,19626,19627],{},"✅ Created a functional MCP server from scratch",[73,19629,19630],{},"✅ Integrated real-time weather data from an external API",[73,19632,19633],{},"✅ Connected it to VS Code and GitHub Copilot",[73,19635,19636],{},"✅ Learned the fundamentals of the Model Context Protocol",[11,19638,19639],{},[58,19640,19641],{},"Key Takeaways:",[70,19643,19644,19650,19656,19662],{},[73,19645,19646,19649],{},[58,19647,19648],{},"Simplicity:"," MCP servers are much easier to build than they appear",[73,19651,19652,19655],{},[58,19653,19654],{},"Power:"," Real data makes AI interactions dramatically more valuable",[73,19657,19658,19661],{},[58,19659,19660],{},"Flexibility:"," The same server works across multiple AI platforms",[73,19663,19664,19667],{},[58,19665,19666],{},"Future-ready:"," You're building the infrastructure for next-gen AI",[11,19669,19670,19672],{},[58,19671,5942],{}," The possibilities are endless! Weather was just the beginning, now you can connect AI to databases, APIs, file systems, and any service you can imagine.",[11,19674,17189],{},[23,19676,19678],{"id":19677},"resources-and-further-reading","📚 Resources and Further Reading",[11,19680,19681],{},[58,19682,19683],{},"Official Documentation",[70,19685,19686,19693,19700,19707,19714,19721],{},[73,19687,19688,19692],{},[15,19689,19691],{"href":17163,"rel":19690},[19],"Model Context Protocol Documentation"," - Complete MCP reference",[73,19694,19695],{},[15,19696,19699],{"href":19697,"rel":19698},"https://github.com/modelcontextprotocol/typescript-sdk",[19],"TypeScript SDK",[73,19701,19702],{},[15,19703,19706],{"href":19704,"rel":19705},"https://github.com/modelcontextprotocol/python-sdk",[19],"Python SDK",[73,19708,19709],{},[15,19710,19713],{"href":19711,"rel":19712},"https://github.com/modelcontextprotocol/java-sdk",[19],"Java SDK",[73,19715,19716],{},[15,19717,19720],{"href":19718,"rel":19719},"https://github.com/modelcontextprotocol/kotlin-sdk",[19],"Kotlin SDK",[73,19722,19723],{},[15,19724,19727],{"href":19725,"rel":19726},"https://github.com/modelcontextprotocol/csharp-sdk",[19],"C# SDK",[11,19729,19730],{},[58,19731,19732],{},"APIs Used in This Tutorial",[70,19734,19735,19742],{},[73,19736,19737,19741],{},[15,19738,19740],{"href":18271,"rel":19739},[19],"Open-Meteo Weather API"," - Free weather data service",[73,19743,19744,19749],{},[15,19745,19748],{"href":19746,"rel":19747},"https://zod.dev/",[19],"Zod Documentation"," - TypeScript schema validation",[11,19751,19752],{},[58,19753,19754],{},"MCP Examples",[70,19756,19757,19764,19770],{},[73,19758,19759,19763],{},[15,19760,19762],{"href":17170,"rel":19761},[19],"MCP Examples Repository"," - Sample servers",[73,19765,19766,19769],{},[15,19767,5713],{"href":5582,"rel":19768},[19]," - Provides browser automation capabilities using Playwright.",[73,19771,19772],{},[15,19773,19776],{"href":19774,"rel":19775},"https://docs.github.com/en/copilot/how-tos/context/model-context-protocol/using-the-github-mcp-server",[19],"GitHub MCP",[11,19778,19779,17013,19782],{},[58,19780,19781],{},"Demo Repo",[15,19783,19786],{"href":19784,"rel":19785},"https://github.com/debs-obrien/mcp-weather-server-demo",[19],"Demo available on GitHub",[11,19788,19789],{},[58,19790,19791],{},"Watch the Video",[11,19793,19794],{},[15,19795,19798],{"href":19796,"rel":19797},"https://www.youtube.com/watch?v=egVm_z1nnnQ",[19],"Build your first MCP Server: Tutorial for Beginners.",[11,19800,19801,19802],{},"Shoutout to Miguel Angel Duran for his great course and explanation. Check out his video in Spanish for a similar demo: ",[15,19803,19806],{"href":19804,"rel":19805},"https://youtu.be/wnHczxwukYY?si=6TlZiYpc_XKkn46v",[19],"Learn MCP! For Beginners + Create Our First MCP From Scratch\"",[2011,19808,19809],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":19811},[19812,19813,19818,19822,19829,19833,19838,19842,19843,19844,19848,19849],{"id":17270,"depth":748,"text":17271},{"id":17297,"depth":748,"text":17298,"children":19814},[19815,19816,19817],{"id":17304,"depth":756,"text":17305},{"id":17337,"depth":756,"text":17338},{"id":17357,"depth":756,"text":17358},{"id":17461,"depth":748,"text":17462,"children":19819},[19820,19821],{"id":17468,"depth":756,"text":17469},{"id":17488,"depth":756,"text":17489},{"id":17553,"depth":748,"text":17554,"children":19823},[19824,19825,19826,19827,19828],{"id":17564,"depth":756,"text":17565},{"id":17613,"depth":756,"text":17614},{"id":17663,"depth":756,"text":17664},{"id":17858,"depth":756,"text":17859},{"id":17921,"depth":756,"text":17922},{"id":18147,"depth":748,"text":18148,"children":19830},[19831,19832],{"id":18154,"depth":756,"text":18155},{"id":18209,"depth":756,"text":18210},{"id":18264,"depth":748,"text":18265,"children":19834},[19835,19836,19837],{"id":18277,"depth":756,"text":18278},{"id":18298,"depth":756,"text":18299},{"id":18716,"depth":756,"text":18717},{"id":18742,"depth":748,"text":18743,"children":19839},[19840,19841],{"id":18749,"depth":756,"text":18750},{"id":18902,"depth":756,"text":18903},{"id":18972,"depth":748,"text":18973},{"id":19031,"depth":748,"text":19032},{"id":19494,"depth":748,"text":19495,"children":19845},[19846,19847],{"id":19501,"depth":756,"text":19502},{"id":19601,"depth":756,"text":19602},{"id":3293,"depth":748,"text":3294},{"id":19677,"depth":748,"text":19678},"2025-07-01","Have you ever wanted your AI assistant to access real-time data? Model Context Protocol (MCP) servers make this possible, and they're surprisingly simple to build and use! Learn how to build an MCP server from scratch using TypeScript, connect it to a real weather API, and integrate it with VS Code and GitHub Copilot.",{"loading":3458},"/blog/building-your-first-mcp-server-a-beginners-tutorial",{"title":17218,"description":19851},"blog/building-your-first-mcp-server-a-beginners-tutorial",[3321,796,795,15559],"Xn69KvPoAFcDPry5U0V-h7OCswm0-vRzAhZWCOuEXuM",{"id":2054,"title":2055,"body":19859,"canonical":788,"date":2717,"description":2718,"extension":786,"featured":787,"image":788,"meta":20311,"navigation":790,"ogimage":788,"path":2720,"provider":788,"published":790,"seo":20312,"stem":2722,"tags":20313,"url":788,"__hash__":2725},{"type":8,"value":19860,"toc":20298},[19861,19863,19867,19869,19871,19873,19875,19879,19881,19883,19885,19887,19901,19903,19905,19907,19909,19915,19929,19931,19933,19935,19937,19939,19941,19943,19945,19949,19963,19965,19967,19969,19971,19973,19975,19977,19982,19984,19988,19992,19994,20008,20010,20012,20016,20104,20106,20108,20110,20114,20132,20134,20136,20138,20140,20144,20154,20156,20158,20160,20162,20164,20168,20178,20180,20182,20194,20196,20198,20200,20202,20204,20206,20208,20210,20212,20214,20228,20230,20232,20234,20236,20238,20256,20258,20260,20262,20264,20269,20271,20285,20287,20292,20294,20296],[11,19862,2060],{},[11,19864,19865],{},[15,19866,2066],{"href":2065},[11,19868,2069],{},[11,19870,2072],{},[11,19872,2075],{},[11,19874,2078],{},[11,19876,19877],{},[121,19878],{"alt":2083,"src":2084},[23,19880,2088],{"id":2087},[11,19882,2091],{},[11,19884,2094],{},[11,19886,2097],{},[70,19888,19889,19891,19893,19895,19897,19899],{},[73,19890,2102],{},[73,19892,2105],{},[73,19894,2108],{},[73,19896,2111],{},[73,19898,2114],{},[73,19900,2117],{},[11,19902,2120],{},[11,19904,2123],{},[23,19906,2127],{"id":2126},[11,19908,2130],{},[11,19910,2133,19911,2137,19913,2141],{},[179,19912,2136],{},[179,19914,2140],{},[70,19916,19917,19919,19921,19923,19925,19927],{},[73,19918,2146],{},[73,19920,2149],{},[73,19922,2152],{},[73,19924,2155],{},[73,19926,2158],{},[73,19928,2161],{},[11,19930,2164],{},[11,19932,2167],{},[11,19934,2170],{},[11,19936,2173],{},[23,19938,2177],{"id":2176},[11,19940,2180],{},[11,19942,2183],{},[11,19944,2186],{},[11,19946,2189,19947,2192],{},[179,19948,2140],{},[70,19950,19951,19953,19955,19957,19959,19961],{},[73,19952,2197],{},[73,19954,2200],{},[73,19956,2203],{},[73,19958,2206],{},[73,19960,2209],{},[73,19962,2212],{},[11,19964,2215],{},[11,19966,2218],{},[11,19968,2221],{},[11,19970,2224],{},[23,19972,2228],{"id":2227},[11,19974,2231],{},[11,19976,2234],{},[299,19978,19980],{"className":19979,"code":2238,"language":304,"meta":307},[302],[179,19981,2238],{"__ignoreMap":307},[11,19983,2243],{},[23,19985,19986,2249],{"id":2246},[179,19987,2140],{},[11,19989,2252,19990,2255],{},[179,19991,2140],{},[11,19993,2258],{},[2260,19995,19996,19998,20000,20002,20004,20006],{},[73,19997,2264],{},[73,19999,2267],{},[73,20001,2270],{},[73,20003,2273],{},[73,20005,2276],{},[73,20007,2279],{},[11,20009,2282],{},[11,20011,2285],{},[11,20013,2288,20014,2291],{},[179,20015,2140],{},[299,20017,20018],{"className":2294,"code":2295,"language":2296,"meta":307,"style":307},[179,20019,20020,20024,20028,20032,20036,20040,20044,20048,20052,20056,20060,20064,20068,20072,20076,20080,20084,20088,20092,20096,20100],{"__ignoreMap":307},[1736,20021,20022],{"class":1738,"line":1739},[1736,20023,2303],{},[1736,20025,20026],{"class":1738,"line":748},[1736,20027,1747],{"emptyLinePlaceholder":790},[1736,20029,20030],{"class":1738,"line":756},[1736,20031,2312],{},[1736,20033,20034],{"class":1738,"line":1755},[1736,20035,2317],{},[1736,20037,20038],{"class":1738,"line":1761},[1736,20039,1747],{"emptyLinePlaceholder":790},[1736,20041,20042],{"class":1738,"line":1767},[1736,20043,2326],{},[1736,20045,20046],{"class":1738,"line":1772},[1736,20047,2331],{},[1736,20049,20050],{"class":1738,"line":1778},[1736,20051,1747],{"emptyLinePlaceholder":790},[1736,20053,20054],{"class":1738,"line":1784},[1736,20055,2340],{},[1736,20057,20058],{"class":1738,"line":1790},[1736,20059,2345],{},[1736,20061,20062],{"class":1738,"line":1796},[1736,20063,2350],{},[1736,20065,20066],{"class":1738,"line":2353},[1736,20067,1747],{"emptyLinePlaceholder":790},[1736,20069,20070],{"class":1738,"line":2358},[1736,20071,2361],{},[1736,20073,20074],{"class":1738,"line":2364},[1736,20075,2367],{},[1736,20077,20078],{"class":1738,"line":2370},[1736,20079,2373],{},[1736,20081,20082],{"class":1738,"line":2376},[1736,20083,1747],{"emptyLinePlaceholder":790},[1736,20085,20086],{"class":1738,"line":2381},[1736,20087,2384],{},[1736,20089,20090],{"class":1738,"line":2387},[1736,20091,2390],{},[1736,20093,20094],{"class":1738,"line":2393},[1736,20095,1747],{"emptyLinePlaceholder":790},[1736,20097,20098],{"class":1738,"line":2398},[1736,20099,2401],{},[1736,20101,20102],{"class":1738,"line":2404},[1736,20103,2407],{},[11,20105,2410],{},[23,20107,2414],{"id":2413},[11,20109,2417],{},[11,20111,2420,20112,2424],{},[179,20113,2423],{},[70,20115,20116,20118,20120,20122,20124,20126,20128,20130],{},[73,20117,2429],{},[73,20119,2432],{},[73,20121,2435],{},[73,20123,2438],{},[73,20125,2441],{},[73,20127,2444],{},[73,20129,2447],{},[73,20131,2450],{},[11,20133,2453],{},[11,20135,2456],{},[23,20137,2460],{"id":2459},[11,20139,2463],{},[11,20141,20142,2469],{},[179,20143,2468],{},[11,20145,20146,2475,20148,2479,20150,2483,20152,2487],{},[179,20147,2474],{},[179,20149,2478],{},[179,20151,2482],{},[179,20153,2486],{},[11,20155,2490],{},[11,20157,2493],{},[23,20159,2497],{"id":2496},[11,20161,2500],{},[11,20163,2503],{},[11,20165,2506,20166,2510],{},[179,20167,2509],{},[70,20169,20170,20172,20174,20176],{},[73,20171,2515],{},[73,20173,2518],{},[73,20175,2521],{},[73,20177,2524],{},[11,20179,2527],{},[11,20181,2530],{},[70,20183,20184,20186,20188,20190,20192],{},[73,20185,2535],{},[73,20187,2538],{},[73,20189,2541],{},[73,20191,2544],{},[73,20193,2547],{},[11,20195,2550],{},[23,20197,2554],{"id":2553},[11,20199,2557],{},[11,20201,2560],{},[11,20203,2563],{},[11,20205,2566],{},[11,20207,2569],{},[11,20209,2572],{},[11,20211,2575],{},[11,20213,2578],{},[70,20215,20216,20218,20220,20222,20224,20226],{},[73,20217,2583],{},[73,20219,2586],{},[73,20221,2589],{},[73,20223,2592],{},[73,20225,2595],{},[73,20227,2598],{},[11,20229,2601],{},[11,20231,2604],{},[11,20233,2607],{},[23,20235,2611],{"id":2610},[11,20237,2614],{},[2260,20239,20240,20244,20246,20248,20250,20252,20254],{},[73,20241,2619,20242,891],{},[179,20243,2140],{},[73,20245,2624],{},[73,20247,2627],{},[73,20249,2630],{},[73,20251,2633],{},[73,20253,2636],{},[73,20255,2639],{},[11,20257,2642],{},[11,20259,2645],{},[23,20261,2649],{"id":2648},[11,20263,2652],{},[11,20265,20266],{},[15,20267,2659],{"href":2657,"rel":20268},[19],[11,20270,2662],{},[299,20272,20273],{"className":2665,"code":2666,"language":2667,"meta":307,"style":307},[179,20274,20275],{"__ignoreMap":307},[1736,20276,20277,20279,20281,20283],{"class":1738,"line":1739},[1736,20278,2675],{"class":2674},[1736,20280,2678],{"class":1935},[1736,20282,2681],{"class":1935},[1736,20284,2684],{"class":1935},[11,20286,2687],{},[299,20288,20290],{"className":20289,"code":2691,"language":304},[302],[179,20291,2691],{"__ignoreMap":307},[11,20293,2696],{},[11,20295,2699],{},[2011,20297,2702],{},{"title":307,"searchDepth":748,"depth":748,"links":20299},[20300,20301,20302,20303,20304,20305,20306,20307,20308,20309,20310],{"id":2087,"depth":748,"text":2088},{"id":2126,"depth":748,"text":2127},{"id":2176,"depth":748,"text":2177},{"id":2227,"depth":748,"text":2228},{"id":2246,"depth":748,"text":2710},{"id":2413,"depth":748,"text":2414},{"id":2459,"depth":748,"text":2460},{"id":2496,"depth":748,"text":2497},{"id":2553,"depth":748,"text":2554},{"id":2610,"depth":748,"text":2611},{"id":2648,"depth":748,"text":2649},{},{"title":2055,"description":2718},[795,2724,796],{"id":20315,"title":20316,"body":20317,"canonical":788,"date":20467,"description":20468,"extension":786,"featured":787,"image":20469,"meta":20470,"navigation":790,"ogimage":788,"path":20472,"provider":3460,"published":787,"seo":20473,"stem":20474,"tags":20475,"url":788,"__hash__":20476},"blog/blog/challenging-yourself.md","Challenging Yourself",{"type":8,"value":20318,"toc":20454},[20319,20322,20325,20329,20332,20338,20342,20345,20349,20352,20358,20362,20365,20371,20375,20378,20382,20385,20389,20392,20398,20402,20405,20408,20414,20418,20421,20424,20430,20432,20435,20438,20444,20448,20451],[11,20320,20321],{},"Yesterday I took on the Gran Fondo Challenge in Mallorca. A cycling challenge that cycles all around the island of Mallorca for a total of 235km with an expected cycling time of 9.5 hours at a speed of 25km per hour. Just the thoughts of that is scary enough. Was I able for it? Had I trained enough for it? Probably not. But I was determined to give it a go. The club told me that at the 150km mark there was a way to go home by train so if I could make it 150km then I would be pretty happy with that.",[11,20323,20324],{},"Looking back, I for sure didn't eat enough the night before. In general I probably should have eaten more carbs through out the whole day. In the morning I got up at 6am and had a small bowl of granola with a non dairy yoghurt followed by 2 small slices of brown toast and peanut butter. And then I drank my homemade chocolate and banana smoothie made with coconut milk. I filled my two 750ml bottles of water and added some electrolytes in them and wrapped up 4 more of these tablets in some tin foil for later and packed 4 energy bars in my back pocket. I then got dressed, put some sun screen on and went down to the garage to get my bike.",[23,20326,20328],{"id":20327},"getting-ready","Getting Ready",[11,20330,20331],{},"I checked my tyres and filled them up a bit more plus oiled the chain. Then I changed my shoes, put my helmet on, grabbed my sunglasses and went outside. The sun was still rising so I had to put my sunglasses on my helmet as couldn't wear them yet. I cycled the 2km to the meeting point outside the Rapha Cafe feeling both nervous and excited at the same time. There were more than 20 of us signed up for the ride. There were four ride leaders including 2 women but apart from the ride leaders I was the only girl in the group. Guys are always stronger than me so I knew this was going to be tough if I wanted to keep up.",[11,20333,20334],{},[121,20335],{"alt":20336,"src":20337},"in cycling gear outside Rapha club","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5372_ckb78i",[23,20339,20341],{"id":20340},"off-we-go","Off we go",[11,20343,20344],{},"We hit the road with me near the front of the peloton. This meant I wouldn't get tailed off at a roundabout but meant I had to work hard to keep that position. We were going at an average of 27km per hour which is above my normal but I just kept pushing and kept my place. However at about 60km in I could feel my body tingling which is never a good sign. I drank some more but I had very little liquid left. Then my leg started to cramp. Something I have never experienced before while cycling. Then the other leg started to cramp. One of the ride leads saw and went cycled off to tell the other ride lead who came back and cycled with me giving me an electrolyte tablet to suck on and giving me some water when I needed. He said we were 10km from the first stop where we would refill and access the situation cause cycling with cramps was not good.",[23,20346,20348],{"id":20347},"first-stop","First stop",[11,20350,20351],{},"I made it to the supermarket stop and went in to buy an energy drink and a 1.5 litre bottle of water. I had drank the energy drink before I had reached the till. I ate an energy bar, refilled my bottles of water and drank another energy drink. The ride lead asked me what I wanted to do and I said I wanted to continue but if I got more cramps they would have to go with out me and I would stop or go at my pace. I asked him if he could share his live location through whatsapp so I could see where they were incase I did drop off.",[11,20353,20354],{},[121,20355],{"alt":20356,"src":20357},"at the supermarket in cycling gear in front of lots of bikes","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5374_mfqmx7",[23,20359,20361],{"id":20360},"on-my-own","On my own",[11,20363,20364],{},"At km 87 going up a hill my leg started to cramp and I just couldn't push to keep up with the guys. I dropped gears and took it really slow up the hill and decided to just keep going for as long as I could. I stopped at 100km to take a photo and make the milestone. I had one water bottle left so was feeling good and decided to continue and try to get to 150km. At the half way mark I stopped again and took another selfie. I had some minor cramps and it was starting to get really hot.",[11,20366,20367],{},[121,20368],{"alt":20369,"src":20370},"me in cycling gear on a road smiling","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5375_evklrv",[23,20372,20374],{"id":20373},"second-stop","Second stop",[11,20376,20377],{},"After 5 hours of cycling I stopped in Porto Cristo to refill at a garage. The guys who worked there asked me how I was and I said I had had better days. They laughed and asked where I had come from and I said Palma, 125km. They said wow I can't even do 20km. I drank an energy drink, filled by bottles with water again and my last electrolyte tablets. Then bought some chocolate and sweets and took 5 minutes to just catch my breath again. I checked where the guys were and they were about 16km ahead of me so not too far really.",[23,20379,20381],{"id":20380},"_150km-mark","150km mark",[11,20383,20384],{},"I set off with the aim of doing another 25km to make it to 150km. For some reason my garmin had not synced the route and it was not showing me where I needed to go so I was relying on the whatsapp live location to figure out where I needed to go. I had to stop at roundabouts and keep checking if I was going the right direction or else I just read the signs and guessed. It wasn't until the next day that I realised I had taken a wrong turn and gone inland a bit and added another 15km to my ride. So instead of the lunch stop being at 150km for me it was actually at 165km. But I kept going as the lads had stopped for lunch and they were only 17km away. I could make it. I knew I could. So I just kept going, kept pushing and when the wind was on my side I was going at over 50km per hour and enjoying every second. And the views were stunning too.",[23,20386,20388],{"id":20387},"caught-up-with-the-lads","Caught up with the lads",[11,20390,20391],{},"After 165km I caught up with the guys who were just finishing paying for their lunch. They had done 150km and had stopped for a sandwiches. As a celiac I can't eat in places like this so there was no point in me stopping. The lads were so impressed to see me and said wow did you just do all that on your own. I said \"yeh. The live location really helped as I knew you were all not too far away.\" So now they were all getting up to go again and I hadn't even stopped but I decided to continue with them and see how far I could make it. We stopped at the supermarket to refill water and energy drinks and I bought some walnuts and ate another energy bar. Not the best meal but hey as a celiac I really am limited in what I can buy especially for quick carbs.",[11,20393,20394],{},[121,20395],{"alt":20396,"src":20397},"me in cycling gear at a cafe with other cyclists getting ready to leave","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5385_dintlt",[23,20399,20401],{"id":20400},"getting-tired","Getting tired",[11,20403,20404],{},"I kept in the peloton as much as I could but going round roundabouts I was tailing off at the end. I wasn't tired but no matter how much I pushed my bike just didn't move as fast as everyone elses. I really am convinced that my bike is slow, it is 10 years old after all. But then again it could be just that I'm slow. Who knows. Then when we had lateral wind while cycling by the sea I was falling behind and one of the lads was helping me by giving me a push so I could keep up with the peloton. But after km 187 I told them I needed to stop. My body needed 5 mins and I told them to go on without me. There were a few small climbs and I was struggling to do them at that pace. I needed to refuel but the thoughts of eating another energy bar were just not satisfying enough so I had some more jellys.",[11,20406,20407],{},"I stopped for 5 minutes and re-accessed the situation. I had now down 187km, just under 7 and a half hours of cycling and I had only eaten 2 energy bars, some chocolate and some jellys. It was hot and my body was sweating so much salt that my cycling gear was practically white. I decided the best thing to do was to call it a day after 200km. I called my husband and asked him to come pick me up so I didn't have to wait for the train which was another valid option if needed. And off I went on my last few kms.",[11,20409,20410],{},[121,20411],{"alt":20412,"src":20413},"me in cycling gear in the countryside","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5386_rp8hwx",[23,20415,20417],{"id":20416},"_200km","200km",[11,20419,20420],{},"At 200km I put my arm up in the arm as if I had won a race. The cars going by probably didn't know what to think. I was feeling so proud. 200km. New record. So after 200km, 8 hours of cycling and a total of 10 hours on the go I got into the car and headed home. The funny thing is at that point I actually could have continued. I hadn't reached my limit. It's like reaching 200km had given me this new found energy that I didn't have when on km 187. Also the downhills really help your legs recover. But I was so happy to have reached 200km that I didn't want to push it any further.",[11,20422,20423],{},"I was feeling tired, excited, happy, hungry, sweaty, soar but most of all proud. My husband was also so proud of me. Neither I nor him actually thought I would get so far.",[11,20425,20426],{},[121,20427],{"alt":20428,"src":20429},"in cycling gear with my hand in the air smiling","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5390_ehxpyo",[23,20431,6156],{"id":6155},[11,20433,20434],{},"If I compare myself to the group I was the worst and the only person who dropped out and didn't finish. When we compare ourselves to others we normally have unfair comparisons. The guys are stronger than me just for being guys. The female ride leads are ride leads and are cycling hundreds of km more than me and are extremely hard core in my eyes. They all have more modern bikes than me although I have no idea if that helps or not. And they all cycle more than me cause I am away so much and involved in other sports too, I don't do a regular 3 times a week cycling.",[11,20436,20437],{},"One thing I loved while doing this route was sharing my journey over twitter. I was checking my phone, and seeing the messages of support really helped. The fact that everyone thought it was incredible that I had done 200km really helped me see my achievement. Also one of the ride leaders at lunch time told me that it was so impressive that I had kept going on my own for such a long distance and caught up with them. And at that point none of us had realised that I had actually done an extra 15km so I guess it was pretty impressive.",[11,20439,20440],{},[121,20441],{"alt":20442,"src":20443},"map of mallorca showing my route and times, 8 hours, 200km","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80/v1648376409/debbie.codes/blog/2022/IMG_5393_e07zrv",[23,20445,20447],{"id":20446},"what-i-learned","What I learned",[11,20449,20450],{},"My mental head space was in great shape. If you can tell yourself you can do something then your body normally can be pushed to the most insane limits. Breaking it down to small achievable goals really helps. Instead of focusing on the end goal you focus on the small ones such as the first stop, the 100km point, the halfway mark etc. Stopping to take a selfie and post on twitter to celebrate the milestone really helps. And the support from others really helps too.",[11,20452,20453],{},"Today I am feeling great like I could do it all over again. Ok a few saddle soars and my legs are like rocks but I feel great and full of energy. Taking a rest day to let my muscles recover although did a half hour swimming cause I just have way too much energy. I am now looking forward to the next challenge and I know whatever it may be I can do it. Challenging yourself in every aspect of life is always totally worth it. Creating goals and aiming for them and when you achieve them the feeling of success is just amazing. I challenge you to challenge yourself, in sport, work, whatever. Enjoy and always have fun.",{"title":307,"searchDepth":748,"depth":748,"links":20455},[20456,20457,20458,20459,20460,20461,20462,20463,20464,20465,20466],{"id":20327,"depth":748,"text":20328},{"id":20340,"depth":748,"text":20341},{"id":20347,"depth":748,"text":20348},{"id":20360,"depth":748,"text":20361},{"id":20373,"depth":748,"text":20374},{"id":20380,"depth":748,"text":20381},{"id":20387,"depth":748,"text":20388},{"id":20400,"depth":748,"text":20401},{"id":20416,"depth":748,"text":20417},{"id":6155,"depth":748,"text":6156},{"id":20446,"depth":748,"text":20447},"2022-09-11","Cycle all around the island of Mallorca for a total of 235km with an expected cycling time of 9.5 hours at a speed of 25km per hour. Just the thoughts of that is scary enough. Was I able for it? Had I trained enough for it? Probably not. But I was determined to give it a go.","v1648376409/debbie.codes/blog/2022/IMG_5391_pp2gha",{"ogImage":20471,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648376409/debbie.codes/blog/2022/IMG_5391_pp2gha","/blog/challenging-yourself",{"title":20316,"description":20468},"blog/challenging-yourself",[3464,5495],"6RxzM8Tml4llXoClu3qrlHMQ5H9VXOsdincRULt2ELM",{"id":20478,"title":20479,"body":20480,"canonical":788,"date":20484,"description":20485,"extension":786,"featured":787,"image":20486,"meta":20487,"navigation":790,"ogimage":788,"path":20489,"provider":3460,"published":787,"seo":20490,"stem":20491,"tags":20492,"url":20493,"__hash__":20494},"blog/blog/cityjs-conf.md","CityJSConf talks to…Debbie O’Brien",{"type":8,"value":20481,"toc":20482},[],{"title":307,"searchDepth":748,"depth":748,"links":20483},[],"2020-09-07","“I guess you could say I love the buzz of the stage but also because my talks help people learn something new. Now the virtual world is not the same at all, but what can we do? I don’t dislike it — I would just prefer to see and talk to people. I am very much a people’s person.”","fl_lossy,f_auto/v1607270077/debbie.codes/blog/cityjsconf_ozoe4u",{"platform":20488},"CityJS Conf","/blog/cityjs-conf",{"title":20479,"description":20485},"blog/cityjs-conf",[3464],"https://medium.com/cityjsconf/cityjsconf-talks-to-debbie-obrien-3a3a2cb9a5cd","9dcy4Km4XFAE8_d6zjjV0wINJawFY5kaEutm0EdMk08",{"id":1415,"title":1416,"body":20496,"canonical":788,"date":2045,"description":2046,"extension":786,"featured":787,"image":788,"meta":20902,"navigation":790,"ogimage":788,"path":2048,"provider":788,"published":790,"seo":20903,"stem":2050,"tags":20904,"url":788,"__hash__":2052},{"type":8,"value":20497,"toc":20880},[20498,20505,20507,20511,20517,20521,20527,20529,20533,20537,20541,20545,20549,20553,20575,20580,20582,20586,20590,20594,20598,20602,20608,20610,20612,20616,20618,20620,20628,20650,20652,20654,20658,20662,20664,20666,20668,20672,20678,20684,20688,20694,20742,20748,20750,20752,20758,20760,20766,20770,20774,20776,20778,20792,20802,20858,20866,20868,20876,20878],[11,20499,1421,20500,1429],{},[15,20501,20503],{"href":1424,"rel":20502},[19],[133,20504,1428],{},[23,20506,1433],{"id":1432},[138,20508,20509,1440],{"id":1436},[179,20510,1439],{},[11,20512,20513,1445,20515,1449],{},[179,20514,1439],{},[179,20516,1448],{},[138,20518,20519,1456],{"id":1452},[179,20520,1455],{},[11,20522,1459,20523,1462,20525,1466],{},[179,20524,1455],{},[58,20526,1465],{},[11,20528,1469],{},[138,20530,20531,1476],{"id":1472},[179,20532,1475],{},[11,20534,1479,20535,1482],{},[179,20536,1475],{},[138,20538,20539,1489],{"id":1485},[179,20540,1488],{},[11,20542,20543,1494],{},[179,20544,1488],{},[138,20546,20547,1501],{"id":1497},[179,20548,1500],{},[11,20550,1504,20551,1507],{},[179,20552,1500],{},[70,20554,20555,20559,20563,20567,20571],{},[73,20556,20557],{},[58,20558,1514],{},[73,20560,20561],{},[58,20562,1519],{},[73,20564,20565],{},[58,20566,1524],{},[73,20568,20569],{},[58,20570,1529],{},[73,20572,20573],{},[58,20574,1534],{},[299,20576,20578],{"className":20577,"code":1538,"language":304},[302],[179,20579,1538],{"__ignoreMap":307},[11,20581,1543],{},[138,20583,20584,1550],{"id":1546},[179,20585,1549],{},[11,20587,20588,1555],{},[179,20589,1549],{},[138,20591,20592,1562],{"id":1558},[179,20593,1561],{},[11,20595,1565,20596,1568],{},[179,20597,1561],{},[138,20599,20600,1575],{"id":1571},[179,20601,1574],{},[11,20603,1578,20604,1581,20606,1585],{},[179,20605,1574],{},[58,20607,1584],{},[23,20609,1589],{"id":1588},[138,20611,1593],{"id":1592},[11,20613,1596,20614,1600],{},[58,20615,1599],{},[11,20617,1603],{},[138,20619,1607],{"id":1606},[11,20621,1596,20622,1612,20624,1616,20626,1620],{},[58,20623,1599],{},[58,20625,1615],{},[58,20627,1619],{},[70,20629,20630,20634,20638,20642,20646],{},[73,20631,20632,1628],{},[58,20633,1627],{},[73,20635,20636,1634],{},[58,20637,1633],{},[73,20639,20640,1640],{},[58,20641,1639],{},[73,20643,20644,1646],{},[58,20645,1645],{},[73,20647,20648,1652],{},[58,20649,1651],{},[11,20651,1655],{},[138,20653,1659],{"id":1658},[11,20655,1662,20656,1666],{},[58,20657,1665],{},[11,20659,1669,20660,1673],{},[58,20661,1672],{},[11,20663,1676],{},[138,20665,1680],{"id":1679},[11,20667,1683],{},[138,20669,1687,20670],{"id":1686},[179,20671,1690],{},[11,20673,1693,20674,1696,20676,1700],{},[179,20675,1690],{},[179,20677,1699],{},[11,20679,1703,20680,1707,20682,1711],{},[179,20681,1706],{},[58,20683,1710],{},[1713,20685,20686],{},[11,20687,1717],{},[11,20689,1720,20690,1724,20692,1727],{},[58,20691,1723],{},[58,20693,1619],{},[299,20695,20696],{"className":1730,"code":1731,"language":1732,"meta":307,"style":307},[179,20697,20698,20702,20706,20710,20714,20718,20722,20726,20730,20734,20738],{"__ignoreMap":307},[1736,20699,20700],{"class":1738,"line":1739},[1736,20701,1742],{},[1736,20703,20704],{"class":1738,"line":748},[1736,20705,1747],{"emptyLinePlaceholder":790},[1736,20707,20708],{"class":1738,"line":756},[1736,20709,1752],{},[1736,20711,20712],{"class":1738,"line":1755},[1736,20713,1758],{},[1736,20715,20716],{"class":1738,"line":1761},[1736,20717,1764],{},[1736,20719,20720],{"class":1738,"line":1767},[1736,20721,1747],{"emptyLinePlaceholder":790},[1736,20723,20724],{"class":1738,"line":1772},[1736,20725,1775],{},[1736,20727,20728],{"class":1738,"line":1778},[1736,20729,1781],{},[1736,20731,20732],{"class":1738,"line":1784},[1736,20733,1787],{},[1736,20735,20736],{"class":1738,"line":1790},[1736,20737,1793],{},[1736,20739,20740],{"class":1738,"line":1796},[1736,20741,1799],{},[11,20743,1802,20744,1806,20746,1810],{},[58,20745,1805],{},[58,20747,1809],{},[11,20749,1813],{},[138,20751,1817],{"id":1816},[11,20753,1820,20754,1824,20756,1828],{},[58,20755,1823],{},[179,20757,1827],{},[138,20759,1832],{"id":1831},[11,20761,1596,20762,1837,20764,1841],{},[58,20763,1672],{},[179,20765,1840],{},[11,20767,1844,20768,1847],{},[179,20769,1574],{},[11,20771,1850,20772,1854],{},[179,20773,1853],{},[138,20775,1858],{"id":1857},[11,20777,1861],{},[70,20779,20780,20784,20788],{},[73,20781,20782,1869],{},[58,20783,1868],{},[73,20785,20786,1875],{},[58,20787,1874],{},[73,20789,20790,1881],{},[58,20791,1880],{},[11,20793,1884,20794,1888,20796,1892,20798,1896,20800,1900],{},[179,20795,1887],{},[179,20797,1891],{},[179,20799,1895],{},[179,20801,1899],{},[299,20803,20804],{"className":1903,"code":1904,"language":1905,"meta":307,"style":307},[179,20805,20806,20810,20816,20822,20828,20832,20836,20842,20846,20850,20854],{"__ignoreMap":307},[1736,20807,20808],{"class":1738,"line":1739},[1736,20809,1913],{"class":1912},[1736,20811,20812,20814],{"class":1738,"line":748},[1736,20813,1919],{"class":1918},[1736,20815,1922],{"class":1912},[1736,20817,20818,20820],{"class":1738,"line":756},[1736,20819,1927],{"class":1918},[1736,20821,1930],{"class":1912},[1736,20823,20824,20826],{"class":1738,"line":1755},[1736,20825,1936],{"class":1935},[1736,20827,1939],{"class":1912},[1736,20829,20830],{"class":1738,"line":1761},[1736,20831,1944],{"class":1935},[1736,20833,20834],{"class":1738,"line":1767},[1736,20835,1949],{"class":1912},[1736,20837,20838,20840],{"class":1738,"line":1772},[1736,20839,1954],{"class":1918},[1736,20841,1930],{"class":1912},[1736,20843,20844],{"class":1738,"line":1778},[1736,20845,1961],{"class":1935},[1736,20847,20848],{"class":1738,"line":1784},[1736,20849,1966],{"class":1912},[1736,20851,20852],{"class":1738,"line":1790},[1736,20853,1971],{"class":1912},[1736,20855,20856],{"class":1738,"line":1796},[1736,20857,1976],{"class":1912},[11,20859,1979,20860,1983,20862,1987,20864,891],{},[179,20861,1982],{},[179,20863,1986],{},[179,20865,1990],{},[11,20867,1993],{},[11,20869,1996,20870,1999,20872,2003,20874,891],{},[179,20871,1887],{},[179,20873,2002],{},[179,20875,2006],{},[11,20877,2009],{},[2011,20879,2013],{},{"title":307,"searchDepth":748,"depth":748,"links":20881},[20882,20892],{"id":1432,"depth":748,"text":1433,"children":20883},[20884,20885,20886,20887,20888,20889,20890,20891],{"id":1436,"depth":756,"text":2019},{"id":1452,"depth":756,"text":2021},{"id":1472,"depth":756,"text":2023},{"id":1485,"depth":756,"text":2025},{"id":1497,"depth":756,"text":2027},{"id":1546,"depth":756,"text":2029},{"id":1558,"depth":756,"text":2031},{"id":1571,"depth":756,"text":2033},{"id":1588,"depth":748,"text":1589,"children":20893},[20894,20895,20896,20897,20898,20899,20900,20901],{"id":1592,"depth":756,"text":1593},{"id":1606,"depth":756,"text":1607},{"id":1658,"depth":756,"text":1659},{"id":1679,"depth":756,"text":1680},{"id":1686,"depth":756,"text":2041},{"id":1816,"depth":756,"text":1817},{"id":1831,"depth":756,"text":1832},{"id":1857,"depth":756,"text":1858},{},{"title":1416,"description":2046},[795],{"id":20906,"title":20907,"body":20908,"canonical":788,"date":20912,"description":20913,"extension":786,"featured":787,"image":20914,"meta":20915,"navigation":790,"ogimage":788,"path":20917,"provider":3460,"published":787,"seo":20918,"stem":20919,"tags":20920,"url":20922,"__hash__":20923},"blog/blog/courage-inspiration-developer-community.md","Finding courage and inspiration in the developer community",{"type":8,"value":20909,"toc":20910},[],{"title":307,"searchDepth":748,"depth":748,"links":20911},[],"2022-05-09","How do we empower women in tech and equip them with the skills to help them become true leaders? One way is learning from others' successes and failures. Web GDEs—Debbie O'Brien, Julia Miocene, and Glafira Zhur—discuss the value of one to one mentoring and the impact it has made on their own professional and personal development.","v1652110618/debbie.codes/featured-posts/webdev-mentoring_lpmeze.png",{"loading":3458,"platform":20916},"Web.dev","/blog/courage-inspiration-developer-community",{"title":20907,"description":20913},"blog/courage-inspiration-developer-community",[20921],"mentoring","https://web.dev/gde-mentoring/","ReKKcrSl5IU_opEmVK1yRsYFYsLoZt3-lvE8S8ibeEo",{"id":20925,"title":20926,"body":20927,"canonical":788,"date":20931,"description":20932,"extension":786,"featured":787,"image":20933,"meta":20934,"navigation":790,"ogimage":788,"path":20936,"provider":3460,"published":787,"seo":20937,"stem":20938,"tags":20939,"url":20940,"__hash__":20941},"blog/blog/create-a-blog-with-nuxt-content.md","Create a Blog with Nuxt Content",{"type":8,"value":20928,"toc":20929},[],{"title":307,"searchDepth":748,"depth":748,"links":20930},[],"2020-07-02","The content module is a git files based headless CMS that provides powerful features when it comes to write blogs, documentation sites or just adding content to any regular website. In this post we will go through most of the benefits of this module and discover how we can create a blog with it.","v1630862643/debbie.codes/featured-posts/create-blog-nuxt-content_yoae9h",{"platform":20935},"Nuxt","/blog/create-a-blog-with-nuxt-content",{"title":20926,"description":20932},"blog/create-a-blog-with-nuxt-content",[5239],"https://nuxtjs.org/blog/creating-blog-with-nuxt-content","AqGC__5cEiyDy_ThwvrqQwLY-y3sJBbVgRGdHjF2JlA",{"id":20943,"title":20944,"body":20945,"canonical":788,"date":20949,"description":20950,"extension":786,"featured":787,"image":20951,"meta":20952,"navigation":790,"ogimage":788,"path":20953,"provider":3460,"published":787,"seo":20954,"stem":20955,"tags":20956,"url":20957,"__hash__":20958},"blog/blog/creating-a-nuxt-module.md","Creating a Nuxt Module",{"type":8,"value":20946,"toc":20947},[],{"title":307,"searchDepth":748,"depth":748,"links":20948},[],"2020-11-27","Modules are functions that are called sequentially when booting Nuxt. The framework waits for each module to finish before continuing. In this way, modules can customize almost any aspect of your project. Nuxt modules can be incorporated into npm packages. This makes them easy to reuse across projects and to share with the community.","v1630863199/debbie.codes/featured-posts/creating-nuxt-module_w12ktc",{"loading":3458,"platform":20935},"/blog/creating-a-nuxt-module",{"title":20944,"description":20950},"blog/creating-a-nuxt-module",[5239],"https://nuxtjs.org/blog/creating-a-nuxt-module","JkVnl47P5nAujBrNrzI_BXyGAOEur2I9ErXmOi6lV_M",{"id":20960,"title":20961,"body":20962,"canonical":788,"date":21127,"description":21128,"extension":786,"featured":787,"image":21129,"meta":21130,"navigation":790,"ogimage":788,"path":21131,"provider":3460,"published":787,"seo":21132,"stem":21133,"tags":21134,"url":788,"__hash__":21135},"blog/blog/creating-video-courses.md","Creating Video Courses",{"type":8,"value":20963,"toc":21116},[20964,20973,20976,20980,20983,20987,20995,20998,21002,21005,21008,21012,21015,21018,21022,21030,21033,21037,21046,21059,21064,21068,21071,21074,21078,21081,21084,21087,21089],[11,20965,20966,20967,20972],{},"I started out creating courses for ",[15,20968,20971],{"href":20969,"rel":20970},"https://vueschool.io/",[19],"Vue School"," about 2 years ago. I had absolutely no idea how to go about it or where to start. I was sent out a microphone, which of course is key if you are going to record anything. Luckily I had help from Alex and Rolf in how to go about things. Create the project, write out the scripts, send them for review, fix any changes needed, send them for review again and then start recording them. I didn't have to edit my videos so luckily I didn't have to worry about that and my face is not shown in the videos, only the screen, which makes it a lot easier.",[11,20974,20975],{},"The issue was as a non tech person is editing my videos I need to record every paragraph and then code that part, record the voice, code, record the voice, code and so on. This might work for some people but for me I find it quite time consuming. But not on the voice recording part. Thats the part I normally do in one take.",[23,20977,20979],{"id":20978},"i-always-mess-up-the-code","I Always mess up the Code",[11,20981,20982],{},"The coding part I mess up all the time. As I try to copy from the script I end up making so many mistakes like you would not believe. Now you might say mistakes are good and they are good to show but I make too many and I don't realise it until I get to the end and then I open the browser and the app is broken. Ok again you might say why not show this in the video. Well probably because it takes me about 20 minutes to revise all code blocks in the script and find the one error I have and that is not good content to be watching. Then the worst part. I start to not enjoy it and I get frustrated that I make so many mistakes and it kills my confidence. Plus at the end of creating a course I actually feel I have learnt nothing as I am just copying from my script and not actually coding from memory.",[23,20984,20986],{"id":20985},"along-came-youtube","Along came YouTube",[11,20988,20989,20990,20994],{},"When I started my ",[15,20991,20993],{"href":3630,"rel":20992},[19],"YouTube channel"," I was absolutely terrified. I especially didn't feel I was good enough or that people would even watch my videos. Yes I know, but I suffer big time from Imposter Syndrome so I just deal with that voice in my head all the time. When recording YouTube videos I really needed to know my stuff as there is no script, no way of copying, plus my face is in the video so you can see if I start to look at other screens and copy things. It was challenging at first but I soon came to see that if I knew my material a lot better, then I wouldn't make so many mistakes, because I would actually know what I was doing. It took a bit longer in the research and testing things out phase but then recording the videos was so much easier. Most of my mistakes were then me saying things wrong as opposed to typing things wrong. I also felt great after it as I felt I had learnt something and came away from it being a lot more confident and it took a lot less time to create the content.",[11,20996,20997],{},"I don't edit my YouTube videos. What you see is what you get. I find it easier that way as I can release more content. Perhaps my videos are not very professional compared to edited ones but they are for sure honest and what you see is what you get. And if I make a mistake I just fix it live as I know the code I am working with.",[23,20999,21001],{"id":21000},"then-there-were-live-streams","Then there were Live Streams",[11,21003,21004],{},"I started to do live streams which is the most terrifying thing I have ever done. Put me on a stage to give a talk in front of a million people and I would be much more comfortable than doing a live stream with only 20 people watching. I love live streams where I talk or teach and someone else codes but when I have to code, different ball game altogether. That is probably because I am much better at talking than I am at coding. I am quite aware that I only learnt JavaScript just over 3 years ago so I sometimes make rookie mistakes or just don't know how to do something that is probably quite simple. When you live stream you open yourself up to all those vulnerabilities and the world is watching and they expect you to know everything and be perfect when in fact you are just learning things like everyone else is.",[11,21006,21007],{},"I have done a few live streams and some things didn't work out but I manged to find the solution. I know I always do find the solution but when on a live stream the pressure is on to find it fast as people are watching. But what I have learnt is that people are actually on your side and therefore they are quite helpful in the chat and sometimes point you in the right direction which is great. But it is still terrifying.",[23,21009,21011],{"id":21010},"always-problems","Always problems",[11,21013,21014],{},"I did try to use Twitch and somehow messed it up. Sometimes I wish there was a course on how to stream so you know how to do things right and setup things so they actually work how they should. I got scared and then just decided to only use YouTube. I also had problems with YouTube where I set up the first stream and the link I set up was different to the one that was live which was a nightmare. I still don't know how to fix that so I just don't advertise when I am going live and just go live. I really need to learn how to do that.",[11,21016,21017],{},"My first stream, my computer had issues which I later learnt was from a cable charging issue and so I froze towards the end of the stream and couldn't see anything or click anything which was really hard. I guess we just all have issues we need to overcome but it isn't easy to get back up the next day and do it all over again. So yes I really want to start using Twitch again and doing more live streams. I just have to push forward and keep making those mistakes until I eventually get it right.",[23,21019,21021],{"id":21020},"new-way-of-recording","New way of Recording",[11,21023,21024,21025,21029],{},"Recently I was asked to create the video course for ",[15,21026,21028],{"href":3762,"rel":21027},[19],"Getting Started with Nuxt"," for Netlify's Jamstack explorers site. I was completely honoured to be honest and I do really like creating course content, but I needed it to be not so time consuming and I was told that I had to also edit my own videos too. How could I create a course in less time but still give good quality content and a professional look and feel?",[11,21031,21032],{},"The first videos took too long. I was doing it the same as I was used to. Writing the script and recording in blocks and paragraphs. Something had to change. With this course it is only my voice and no face so for sure it makes it easier but I was spending so much time editing all those paragraphs and code together and so many mistakes too.",[23,21034,21036],{"id":21035},"descript-to-the-rescue","Descript to the Rescue",[11,21038,21039,21040,21045],{},"Thats when I started trying out ",[15,21041,21044],{"href":21042,"rel":21043},"https://www.descript.com/",[19],"Descript",". You see I know what I wanted to do. I wanted to make the course like my YouTube videos but without all the like, em, uh and when I talk without thinking it doesn't always sound so professional. So I tried a new way of doing things. For me it made more sense to code how you would normally code. Not from a script. Just code the whole lesson out. And I did it while speaking just like I do in my YouTube videos. Obviously I had practiced the code a lot and knew my material. So here is the trick. I recorded that first take using ScreenFlow. I now had a pretty good coded video with some minor typing mistakes but it was pretty good.",[11,21047,21048,21049,21052,21053,21058],{},"However my voice recording was not how I wanted it to be. So I exported the video to mp4 and then uploaded it to ",[15,21050,21044],{"href":21042,"rel":21051},[19]," and pressed the transcribe button. I now had my whole script just laid out in front of me. Voice recognition is about 95% so it was not perfect but it was pretty good and much quicker than writing out a script yourself. I then copied the text and put it into ",[15,21054,21057],{"href":21055,"rel":21056},"https://www.notion.so",[19],"Notion"," and started editing it by making it sound more professional and easier to understand.",[11,21060,21061],{},[121,21062],{"alt":21044,"src":21063},"https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto,w_1200/v1612090624/debbie.codes/blog/Screenshot_2021-01-31_at_11.54.39_mlegoc.png",[23,21065,21067],{"id":21066},"time-to-record-again","Time to record again",[11,21069,21070],{},"I now had a script of just text with no code blocks and for me recording that is pretty easy. So I use ScreenFlow again to record my voice only. And all in one take, sometimes a few mistakes but they can be edited later and in the space of minutes I had a really nice voice recording with no typing sounds either.",[11,21072,21073],{},"I then added this to the timeline in ScreenFlow and deleted the first audio track. And then all I had to do was edit it so that the coding was aligned with the text. This did take some time to be honest but it wasn't difficult and I soon learnt new tricks such as freezing frames so the code stays longer on the screen or slowing things down so it matches exactly what you are saying. If something is missing or wrong it is easy to just record that part again and drop it into the timeline.",[23,21075,21077],{"id":21076},"nothing-is-every-perfect","Nothing is every perfect",[11,21079,21080],{},"For sure it is not perfect and it will only get better from here. But I was able to deliver the course in about 2 weeks. Considering there are about 10 videos and I had to create all content from scratch, record screen and voice and edit it, I am pretty happy that it took only 2 weeks. I can assure you I have spent a lot more time in the past when recording and I didn't even have to edit them.",[11,21082,21083],{},"I did find towards the end that it was much easier to edit the audio file before dragging it into the screen timeline. This way you get rid of all the repeated texts first and you know your audio is correct otherwise you sometimes edit and then realise that the voice part is repeated and then you have to delete that and it can take a little longer that way.",[11,21085,21086],{},"Overall I am pretty happy with how I am recording content now and I for sure will be doing it this way forward from now on. Things are always trial and error and what works for me might not work for you as we are all different. There might even be a better way of doing it, who knows! But for now this is how I am creating content and if you find you are taking too long to create content then I suggest giving my method a go. By all means do let me know how you get on or if you have found any other cool tricks that might make things even easier.",[23,21088,5706],{"id":5705},[70,21090,21091,21096,21103,21109],{},[73,21092,21093],{},[15,21094,21044],{"href":21042,"rel":21095},[19],[73,21097,21098],{},[15,21099,21102],{"href":21100,"rel":21101},"https://debbie.codes/resources/courses",[19],"Courses I have Created",[73,21104,21105],{},[15,21106,21108],{"href":3630,"rel":21107},[19],"My YouTube Channel",[73,21110,21111],{},[15,21112,21115],{"href":21113,"rel":21114},"https://www.telestream.net/screenflow/",[19],"Screenflow",{"title":307,"searchDepth":748,"depth":748,"links":21117},[21118,21119,21120,21121,21122,21123,21124,21125,21126],{"id":20978,"depth":748,"text":20979},{"id":20985,"depth":748,"text":20986},{"id":21000,"depth":748,"text":21001},{"id":21010,"depth":748,"text":21011},{"id":21020,"depth":748,"text":21021},{"id":21035,"depth":748,"text":21036},{"id":21066,"depth":748,"text":21067},{"id":21076,"depth":748,"text":21077},{"id":5705,"depth":748,"text":5706},"2021-01-31","Creating video courses can be very time consuming and for sure there is no one way to go about it. In this post I share what I have learnt and how I now find it easier to create courses.","c_scale,fl_lossy/v1612090624/debbie.codes/blog/Screenshot_2021-01-31_at_11.54.39_mlegoc",{},"/blog/creating-video-courses",{"title":20961,"description":21128},"blog/creating-video-courses",[3464],"J8uTViUwIfmscVEL9UrGbz8bn4DEApvNvaX9jhJrpSE",{"id":21137,"title":21138,"body":21139,"canonical":788,"date":21827,"description":21828,"extension":786,"featured":787,"image":21829,"meta":21830,"navigation":790,"ogimage":788,"path":21831,"provider":5235,"published":787,"seo":21832,"stem":21833,"tags":21834,"url":788,"__hash__":21835},"blog/blog/cypress-setup.md","Setting up cypress",{"type":8,"value":21140,"toc":21825},[21141,21149,21152,21169,21172,21188,21191,21239,21242,21302,21311,21327,21330,21389,21398,21571,21580,21596,21599,21602,21655,21658,21667,21670,21682,21685,21688,21701,21704,21721,21724,21799,21802,21805,21822],[11,21142,21143,21148],{},[15,21144,21147],{"href":21145,"rel":21146},"https://www.cypress.io/",[19],"Cypress"," is a great way of testing your frontend application with end to end tests and it is so nice to work with. Writing the tests and making sure they get ran on each build is really important too. Let's take a look at how you setup Cypress in your Nuxt.js project, setup a github action for continuous integration so that Netlify will run the tests every time your application is building.",[11,21150,21151],{},"Once you have already created your Nuxt.js project you can install Cypress as a dev dependency.",[299,21153,21155],{"className":2665,"code":21154,"language":2667,"meta":307,"style":307},"yarn add cypress -D\n",[179,21156,21157],{"__ignoreMap":307},[1736,21158,21159,21161,21163,21166],{"class":1738,"line":1739},[1736,21160,6575],{"class":2674},[1736,21162,2681],{"class":1935},[1736,21164,21165],{"class":1935}," cypress",[1736,21167,21168],{"class":1918}," -D\n",[11,21170,21171],{},"You then need to open Cypress which will launch the test runner and create the cypress folder in your project with some example tests.",[299,21173,21175],{"className":2665,"code":21174,"language":2667,"meta":307,"style":307},"yarn run cypress open\n",[179,21176,21177],{"__ignoreMap":307},[1736,21178,21179,21181,21183,21185],{"class":1738,"line":1739},[1736,21180,6575],{"class":2674},[1736,21182,16130],{"class":1935},[1736,21184,21165],{"class":1935},[1736,21186,21187],{"class":1935}," open\n",[11,21189,21190],{},"In your cypress.json file you can add custom configurations so the viewport is wider and the height depending on your screen or how you want to see your application within the cypress window. We can also set a baseUrl for our test. You will find this file at the root of your project.",[299,21192,21195],{"className":1903,"code":21193,"filename":21194,"language":1905,"meta":307,"style":307},"{\n  \"baseUrl\": \"http://localhost:3000\",\n  \"viewportWidth\": 1600,\n  \"viewportHeight\": 1400\n}\n","cypress.json",[179,21196,21197,21201,21213,21225,21235],{"__ignoreMap":307},[1736,21198,21199],{"class":1738,"line":1739},[1736,21200,1913],{"class":1912},[1736,21202,21203,21206,21208,21211],{"class":1738,"line":748},[1736,21204,21205],{"class":1918},"  \"baseUrl\"",[1736,21207,3065],{"class":1912},[1736,21209,21210],{"class":1935},"\"http://localhost:3000\"",[1736,21212,1939],{"class":1912},[1736,21214,21215,21218,21220,21223],{"class":1738,"line":756},[1736,21216,21217],{"class":1918},"  \"viewportWidth\"",[1736,21219,3065],{"class":1912},[1736,21221,21222],{"class":1918},"1600",[1736,21224,1939],{"class":1912},[1736,21226,21227,21230,21232],{"class":1738,"line":1755},[1736,21228,21229],{"class":1918},"  \"viewportHeight\"",[1736,21231,3065],{"class":1912},[1736,21233,21234],{"class":1918},"1400\n",[1736,21236,21237],{"class":1738,"line":1761},[1736,21238,1976],{"class":1912},[11,21240,21241],{},"We can now remove the examples folder and create our own test inside the integration folder. This is just a basic test will make visit the home page.",[299,21243,21246],{"className":8734,"code":21244,"filename":21245,"language":8736,"meta":307,"style":307},"describe('The Home Page', () => {\n  it('should visit the home page', () => {\n    cy.visit('/')\n  })\n})\n","home.spec.js",[179,21247,21248,21263,21279,21294,21298],{"__ignoreMap":307},[1736,21249,21250,21252,21254,21257,21259,21261],{"class":1738,"line":1739},[1736,21251,17717],{"class":2674},[1736,21253,7751],{"class":1912},[1736,21255,21256],{"class":1935},"'The Home Page'",[1736,21258,10524],{"class":1912},[1736,21260,7013],{"class":4866},[1736,21262,4914],{"class":1912},[1736,21264,21265,21268,21270,21273,21275,21277],{"class":1738,"line":748},[1736,21266,21267],{"class":2674},"  it",[1736,21269,7751],{"class":1912},[1736,21271,21272],{"class":1935},"'should visit the home page'",[1736,21274,10524],{"class":1912},[1736,21276,7013],{"class":4866},[1736,21278,4914],{"class":1912},[1736,21280,21281,21284,21287,21289,21292],{"class":1738,"line":756},[1736,21282,21283],{"class":1912},"    cy.",[1736,21285,21286],{"class":2674},"visit",[1736,21288,7751],{"class":1912},[1736,21290,21291],{"class":1935},"'/'",[1736,21293,7045],{"class":1912},[1736,21295,21296],{"class":1738,"line":1755},[1736,21297,13147],{"class":1912},[1736,21299,21300],{"class":1738,"line":1761},[1736,21301,10582],{"class":1912},[11,21303,21304,21305,21310],{},"If you have eslint setup you may notice that it is giving you errors and saying that it can't find cypress. To fix this we can install the ",[15,21306,21309],{"href":21307,"rel":21308},"https://github.com/cypress-io/eslint-plugin-cypress",[19],"eslint plugin for cypress"," as a dev dependency.",[299,21312,21314],{"className":2665,"code":21313,"language":2667,"meta":307,"style":307},"yarn add eslint-plugin-cypress -D\n",[179,21315,21316],{"__ignoreMap":307},[1736,21317,21318,21320,21322,21325],{"class":1738,"line":1739},[1736,21319,6575],{"class":2674},[1736,21321,2681],{"class":1935},[1736,21323,21324],{"class":1935}," eslint-plugin-cypress",[1736,21326,21168],{"class":1918},[11,21328,21329],{},"We then add this to our .eslintrc to tell it to use the cypress plugin and extend the cypress recommended configuration or alternatively you can set your own rules. We also set our env for cypress globals to true to allow certain globals provided by Cypress.",[299,21331,21334],{"className":1903,"code":21332,"filename":21333,"language":1905,"meta":307,"style":307},"{\n  \"plugins\": [\"cypress\"],\n  \"extends\": [\"plugin:cypress/recommended\"],\n  \"env\": {\n    \"cypress/globals\": true\n  }\n}\n",".eslintrc",[179,21335,21336,21340,21352,21364,21371,21381,21385],{"__ignoreMap":307},[1736,21337,21338],{"class":1738,"line":1739},[1736,21339,1913],{"class":1912},[1736,21341,21342,21345,21347,21350],{"class":1738,"line":748},[1736,21343,21344],{"class":1918},"  \"plugins\"",[1736,21346,16439],{"class":1912},[1736,21348,21349],{"class":1935},"\"cypress\"",[1736,21351,16460],{"class":1912},[1736,21353,21354,21357,21359,21362],{"class":1738,"line":756},[1736,21355,21356],{"class":1918},"  \"extends\"",[1736,21358,16439],{"class":1912},[1736,21360,21361],{"class":1935},"\"plugin:cypress/recommended\"",[1736,21363,16460],{"class":1912},[1736,21365,21366,21369],{"class":1738,"line":1755},[1736,21367,21368],{"class":1918},"  \"env\"",[1736,21370,1922],{"class":1912},[1736,21372,21373,21376,21378],{"class":1738,"line":1761},[1736,21374,21375],{"class":1918},"    \"cypress/globals\"",[1736,21377,3065],{"class":1912},[1736,21379,21380],{"class":1918},"true\n",[1736,21382,21383],{"class":1738,"line":1767},[1736,21384,1971],{"class":1912},[1736,21386,21387],{"class":1738,"line":1772},[1736,21388,1976],{"class":1912},[11,21390,21391,21392,21397],{},"We can then setup our ",[15,21393,21396],{"href":21394,"rel":21395},"https://github.com/cypress-io/github-action",[19],"github action"," by creating a workflows folder in our .github folder. Inside the workflows folder create a main.yml file and add the following config with build as the script we want to run from our package.json for building our application and start for the script we want to use to start our application.",[299,21399,21404],{"className":21400,"code":21401,"filename":21402,"language":21403,"meta":307,"style":307},"language-yml shiki shiki-themes github-light github-dark","name: End-to-end tests\non: [push]\njobs:\n  cypress-run:\n    runs-on: ubuntu-16.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v1\n      # Install NPM dependencies, cache them correctly\n      # and run all Cypress tests\n      - name: Cypress run\n        uses: cypress-io/github-action@v2\n        with:\n          browser: chrome\n          headless: true\n          build: npm run generate\n          start: npm run start\n          # quote the url to be safe against YML parsing surprises\n          wait-on: 'http://localhost:3000'\n",".github/workflows/main.yml","yml",[179,21405,21406,21415,21427,21434,21441,21451,21458,21470,21480,21485,21490,21501,21510,21517,21527,21536,21546,21556,21561],{"__ignoreMap":307},[1736,21407,21408,21410,21412],{"class":1738,"line":1739},[1736,21409,15955],{"class":6696},[1736,21411,3065],{"class":1912},[1736,21413,21414],{"class":1935},"End-to-end tests\n",[1736,21416,21417,21420,21422,21425],{"class":1738,"line":748},[1736,21418,21419],{"class":1918},"on",[1736,21421,16439],{"class":1912},[1736,21423,21424],{"class":1935},"push",[1736,21426,8420],{"class":1912},[1736,21428,21429,21432],{"class":1738,"line":756},[1736,21430,21431],{"class":6696},"jobs",[1736,21433,15995],{"class":1912},[1736,21435,21436,21439],{"class":1738,"line":1755},[1736,21437,21438],{"class":6696},"  cypress-run",[1736,21440,15995],{"class":1912},[1736,21442,21443,21446,21448],{"class":1738,"line":1761},[1736,21444,21445],{"class":6696},"    runs-on",[1736,21447,3065],{"class":1912},[1736,21449,21450],{"class":1935},"ubuntu-16.04\n",[1736,21452,21453,21456],{"class":1738,"line":1767},[1736,21454,21455],{"class":6696},"    steps",[1736,21457,15995],{"class":1912},[1736,21459,21460,21463,21465,21467],{"class":1738,"line":1772},[1736,21461,21462],{"class":1912},"      - ",[1736,21464,15955],{"class":6696},[1736,21466,3065],{"class":1912},[1736,21468,21469],{"class":1935},"Checkout\n",[1736,21471,21472,21475,21477],{"class":1738,"line":1778},[1736,21473,21474],{"class":6696},"        uses",[1736,21476,3065],{"class":1912},[1736,21478,21479],{"class":1935},"actions/checkout@v1\n",[1736,21481,21482],{"class":1738,"line":1784},[1736,21483,21484],{"class":6820},"      # Install NPM dependencies, cache them correctly\n",[1736,21486,21487],{"class":1738,"line":1790},[1736,21488,21489],{"class":6820},"      # and run all Cypress tests\n",[1736,21491,21492,21494,21496,21498],{"class":1738,"line":1796},[1736,21493,21462],{"class":1912},[1736,21495,15955],{"class":6696},[1736,21497,3065],{"class":1912},[1736,21499,21500],{"class":1935},"Cypress run\n",[1736,21502,21503,21505,21507],{"class":1738,"line":2353},[1736,21504,21474],{"class":6696},[1736,21506,3065],{"class":1912},[1736,21508,21509],{"class":1935},"cypress-io/github-action@v2\n",[1736,21511,21512,21515],{"class":1738,"line":2358},[1736,21513,21514],{"class":6696},"        with",[1736,21516,15995],{"class":1912},[1736,21518,21519,21522,21524],{"class":1738,"line":2364},[1736,21520,21521],{"class":6696},"          browser",[1736,21523,3065],{"class":1912},[1736,21525,21526],{"class":1935},"chrome\n",[1736,21528,21529,21532,21534],{"class":1738,"line":2370},[1736,21530,21531],{"class":6696},"          headless",[1736,21533,3065],{"class":1912},[1736,21535,21380],{"class":1918},[1736,21537,21538,21541,21543],{"class":1738,"line":2376},[1736,21539,21540],{"class":6696},"          build",[1736,21542,3065],{"class":1912},[1736,21544,21545],{"class":1935},"npm run generate\n",[1736,21547,21548,21551,21553],{"class":1738,"line":2381},[1736,21549,21550],{"class":6696},"          start",[1736,21552,3065],{"class":1912},[1736,21554,21555],{"class":1935},"npm run start\n",[1736,21557,21558],{"class":1738,"line":2387},[1736,21559,21560],{"class":6820},"          # quote the url to be safe against YML parsing surprises\n",[1736,21562,21563,21566,21568],{"class":1738,"line":2393},[1736,21564,21565],{"class":6696},"          wait-on",[1736,21567,3065],{"class":1912},[1736,21569,21570],{"class":1935},"'http://localhost:3000'\n",[11,21572,21573,21574,21579],{},"When running your tests in CI sometimes the server will continue to execute the next command after signalling to start your server and if the server takes time to boot then Cypress might start to visit your local server before it is ready. You can resolve this by adding this npm package, ",[15,21575,21578],{"href":21576,"rel":21577},"https://github.com/bahmutov/start-server-and-test",[19],"start-server-and-test",", as a dev dependency.",[299,21581,21583],{"className":2665,"code":21582,"language":2667,"meta":307,"style":307},"yarn add start-server-and-test -D\n",[179,21584,21585],{"__ignoreMap":307},[1736,21586,21587,21589,21591,21594],{"class":1738,"line":1739},[1736,21588,6575],{"class":2674},[1736,21590,2681],{"class":1935},[1736,21592,21593],{"class":1935}," start-server-and-test",[1736,21595,21168],{"class":1918},[11,21597,21598],{},"Then in our package json we need to create some scripts so that cypress can run. We create the run script the open script and then a test script for when in production passing in the command of the package we just installed and then using the serve command with where our server will be run and then we call the run script that we created.",[11,21600,21601],{},"For development it is pretty much the same except we call the dev script and the cypress open script.",[299,21603,21605],{"className":1903,"code":21604,"filename":17364,"language":1905,"meta":307,"style":307},"\"cy:open\": \"cypress open\",\n\"cy:run\": \"cypress run\",\n\"test:e2e:run\": \"start-server-and-test start http://localhost:3000 cy:run\",\n\"test:e2e:dev\": \"start-server-and-test dev http://localhost:3000 cy:open\",\n",[179,21606,21607,21619,21631,21643],{"__ignoreMap":307},[1736,21608,21609,21612,21614,21617],{"class":1738,"line":1739},[1736,21610,21611],{"class":1935},"\"cy:open\"",[1736,21613,3065],{"class":1912},[1736,21615,21616],{"class":1935},"\"cypress open\"",[1736,21618,1939],{"class":1912},[1736,21620,21621,21624,21626,21629],{"class":1738,"line":748},[1736,21622,21623],{"class":1935},"\"cy:run\"",[1736,21625,3065],{"class":1912},[1736,21627,21628],{"class":1935},"\"cypress run\"",[1736,21630,1939],{"class":1912},[1736,21632,21633,21636,21638,21641],{"class":1738,"line":756},[1736,21634,21635],{"class":1935},"\"test:e2e:run\"",[1736,21637,3065],{"class":1912},[1736,21639,21640],{"class":1935},"\"start-server-and-test start http://localhost:3000 cy:run\"",[1736,21642,1939],{"class":1912},[1736,21644,21645,21648,21650,21653],{"class":1738,"line":1755},[1736,21646,21647],{"class":1935},"\"test:e2e:dev\"",[1736,21649,3065],{"class":1912},[1736,21651,21652],{"class":1935},"\"start-server-and-test dev http://localhost:3000 cy:open\"",[1736,21654,1939],{"class":1912},[11,21656,21657],{},"One thing we need to do before launching our tests in production is to add the videos and screenshots to our .gitignore file. add to gitignore",[299,21659,21661],{"className":5843,"code":21660,"filename":2002,"language":786,"meta":307,"style":307},"cypress/videos cypress/screenshots\n",[179,21662,21663],{"__ignoreMap":307},[1736,21664,21665],{"class":1738,"line":1739},[1736,21666,21660],{"class":1912},[11,21668,21669],{},"We can now launch your tests to make sure it is working correctly.",[299,21671,21673],{"className":2665,"code":21672,"language":2667,"meta":307,"style":307},"yarn test:e2e:run\n",[179,21674,21675],{"__ignoreMap":307},[1736,21676,21677,21679],{"class":1738,"line":1739},[1736,21678,6575],{"class":2674},[1736,21680,21681],{"class":1935}," test:e2e:run\n",[11,21683,21684],{},"Once you are happy that everything is working as it should you can push your changes which will trigger the github action. Now in your github repo you can click on the actions tab and watch your action do it's job and you can see if tests are passing.",[11,21686,21687],{},"Then there is just one last step in order to get your tests working with your hosting provider.",[11,21689,21690,21691,21694,21695,21700],{},"If you are using ",[133,21692,21693],{},"Netlify"," there is a netlify ",[15,21696,21699],{"href":21697,"rel":21698},"https://github.com/cypress-io/netlify-plugin-cypress#readme",[19],"plugin for cypress"," you can install. In the Netlify dashboard go to the plugins and search for cypress and click install.",[11,21702,21703],{},"Add netlify-plugin-cypress NPM package as a dev dependency to your repository.",[299,21705,21707],{"className":2665,"code":21706,"language":2667,"meta":307,"style":307},"yarn add -D netlify-plugin-cypress\n",[179,21708,21709],{"__ignoreMap":307},[1736,21710,21711,21713,21715,21718],{"class":1738,"line":1739},[1736,21712,6575],{"class":2674},[1736,21714,2681],{"class":1935},[1736,21716,21717],{"class":1918}," -D",[1736,21719,21720],{"class":1935}," netlify-plugin-cypress\n",[11,21722,21723],{},"Then add a netlify.toml file if you haven't already got one and add the following code which will run the commands on build as well as cache the cypress binary in a local node_modules folder so Netlify caches it and then one the site is built it runs the plugin to test the site.",[299,21725,21729],{"className":21726,"code":21727,"language":21728,"meta":307,"style":307},"language-toml shiki shiki-themes github-light github-dark","[build]\n  command = \"npm run generate\"\n  publish = \"dist\"\n\n[build.environment]\n  # cache Cypress binary in local \"node_modules\" folder\n  # so Netlify caches it\n  CYPRESS_CACHE_FOLDER = \"./node_modules/CypressBinary\"\n  # set TERM variable for terminal output\n  TERM = \"xterm\"\n\n[[plugins]]\n  # local Cypress plugin will test our site after it is built\n  package = \"netlify-plugin-cypress\"\n\n","toml",[179,21730,21731,21736,21741,21746,21750,21755,21760,21765,21770,21775,21780,21784,21789,21794],{"__ignoreMap":307},[1736,21732,21733],{"class":1738,"line":1739},[1736,21734,21735],{},"[build]\n",[1736,21737,21738],{"class":1738,"line":748},[1736,21739,21740],{},"  command = \"npm run generate\"\n",[1736,21742,21743],{"class":1738,"line":756},[1736,21744,21745],{},"  publish = \"dist\"\n",[1736,21747,21748],{"class":1738,"line":1755},[1736,21749,1747],{"emptyLinePlaceholder":790},[1736,21751,21752],{"class":1738,"line":1761},[1736,21753,21754],{},"[build.environment]\n",[1736,21756,21757],{"class":1738,"line":1767},[1736,21758,21759],{},"  # cache Cypress binary in local \"node_modules\" folder\n",[1736,21761,21762],{"class":1738,"line":1772},[1736,21763,21764],{},"  # so Netlify caches it\n",[1736,21766,21767],{"class":1738,"line":1778},[1736,21768,21769],{},"  CYPRESS_CACHE_FOLDER = \"./node_modules/CypressBinary\"\n",[1736,21771,21772],{"class":1738,"line":1784},[1736,21773,21774],{},"  # set TERM variable for terminal output\n",[1736,21776,21777],{"class":1738,"line":1790},[1736,21778,21779],{},"  TERM = \"xterm\"\n",[1736,21781,21782],{"class":1738,"line":1796},[1736,21783,1747],{"emptyLinePlaceholder":790},[1736,21785,21786],{"class":1738,"line":2353},[1736,21787,21788],{},"[[plugins]]\n",[1736,21790,21791],{"class":1738,"line":2358},[1736,21792,21793],{},"  # local Cypress plugin will test our site after it is built\n",[1736,21795,21796],{"class":1738,"line":2364},[1736,21797,21798],{},"  package = \"netlify-plugin-cypress\"\n",[11,21800,21801],{},"And thats it. You can now go to your Netlify builds and watch your tests run and your application build. Now if your tests fail your application won't build. This plugin also tells you if it failed because of the tests so it is really easy to see if it is the cause of your builds breaking.",[11,21803,21804],{},"Note: If you are not using Netlify you can modify your generate script to run your tests once the application has been built.",[299,21806,21808],{"className":1903,"code":21807,"filename":17364,"language":1905,"meta":307,"style":307},"\"generate\": \"nuxt generate && cypress install --force && npm run test:e2e:run\",\n",[179,21809,21810],{"__ignoreMap":307},[1736,21811,21812,21815,21817,21820],{"class":1738,"line":1739},[1736,21813,21814],{"class":1935},"\"generate\"",[1736,21816,3065],{"class":1912},[1736,21818,21819],{"class":1935},"\"nuxt generate && cypress install --force && npm run test:e2e:run\"",[1736,21821,1939],{"class":1912},[2011,21823,21824],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":21826},[],"2020-07-17","Let's take a look at how you setup Cypress in your Nuxt.js project, setup a github action for continuous integration so that Netlify will run the tests every time your application is building.","photo-1580982324076-d95230549339?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/cypress-setup",{"title":21138,"description":21828},"blog/cypress-setup",[5239,1411],"xt_H3QwxtpmkGu5RXLZH5CGVLWYLq_onRLx9FlOmxm4",{"id":21837,"title":21838,"body":21839,"canonical":788,"date":22321,"description":22322,"extension":786,"featured":787,"image":22323,"meta":22324,"navigation":790,"ogimage":788,"path":22325,"provider":5235,"published":787,"seo":22326,"stem":22327,"tags":22328,"url":788,"__hash__":22329},"blog/blog/date-formatting.md","Formatting a date in JavaScript",{"type":8,"value":21840,"toc":22316},[21841,21848,21858,21965,21969,21972,22009,22015,22018,22082,22087,22091,22094,22151,22156,22213,22218,22221,22282,22287,22291,22313],[11,21842,21843,21844,21847],{},"When it comes to dates we often add big libraries like Moment.js or Luxon just to format a simple date. But it is actually much simpler than that by using the ",[179,21845,21846],{},"toLocalDateString()"," method. We don't have to install any packages. It just works",[11,21849,21850,21851,21854,21855,21857],{},"In the example below we are using vue so therefore we create a method called ",[179,21852,21853],{},"formatDate()"," and pass in the date that we want to format. We then set our options of how we want the date to be shown. This is an object where we can choose if we want the month to be numeric or long for example. We then return the new date passing in our date we want formatted. We then chain our ",[179,21856,21846],{}," method passing in the language we want to use followed by the options.",[299,21859,21861],{"className":4894,"code":21860,"language":4896,"meta":307,"style":307},"\u003Cscript>\nexport default {\n  methods: {\n    formatDate(date) {\n      const options = { year: 'numeric', month: 'long', day: 'numeric' }\n      return new Date(date).toLocaleDateString('en', options)\n    },\n  }\n};\n\u003C/script>\n\n",[179,21862,21863,21872,21877,21882,21894,21923,21945,21949,21953,21957],{"__ignoreMap":307},[1736,21864,21865,21867,21870],{"class":1738,"line":1739},[1736,21866,6657],{"class":1912},[1736,21868,21869],{"class":6696},"script",[1736,21871,6663],{"class":1912},[1736,21873,21874],{"class":1738,"line":748},[1736,21875,21876],{"class":1912},"export default {\n",[1736,21878,21879],{"class":1738,"line":756},[1736,21880,21881],{"class":1912},"  methods: {\n",[1736,21883,21884,21887,21889,21892],{"class":1738,"line":1755},[1736,21885,21886],{"class":2674},"    formatDate",[1736,21888,7751],{"class":1912},[1736,21890,21891],{"class":5036},"date",[1736,21893,7246],{"class":1912},[1736,21895,21896,21898,21901,21903,21906,21909,21912,21915,21918,21920],{"class":1738,"line":1761},[1736,21897,14312],{"class":4866},[1736,21899,21900],{"class":1918}," options",[1736,21902,4911],{"class":4866},[1736,21904,21905],{"class":1912}," { year: ",[1736,21907,21908],{"class":1935},"'numeric'",[1736,21910,21911],{"class":1912},", month: ",[1736,21913,21914],{"class":1935},"'long'",[1736,21916,21917],{"class":1912},", day: ",[1736,21919,21908],{"class":1935},[1736,21921,21922],{"class":1912}," }\n",[1736,21924,21925,21927,21929,21931,21934,21937,21939,21942],{"class":1738,"line":1767},[1736,21926,15801],{"class":4866},[1736,21928,15674],{"class":4866},[1736,21930,16631],{"class":2674},[1736,21932,21933],{"class":1912},"(date).",[1736,21935,21936],{"class":2674},"toLocaleDateString",[1736,21938,7751],{"class":1912},[1736,21940,21941],{"class":1935},"'en'",[1736,21943,21944],{"class":1912},", options)\n",[1736,21946,21947],{"class":1738,"line":1772},[1736,21948,8553],{"class":1912},[1736,21950,21951],{"class":1738,"line":1778},[1736,21952,1971],{"class":1912},[1736,21954,21955],{"class":1738,"line":1784},[1736,21956,15179],{"class":1912},[1736,21958,21959,21961,21963],{"class":1738,"line":1790},[1736,21960,8105],{"class":1912},[1736,21962,21869],{"class":6696},[1736,21964,6663],{"class":1912},[23,21966,21968],{"id":21967},"different-options","Different Options",[11,21970,21971],{},"We can then use our method like we would use any Vue method in our template passing in the date want formatted",[299,21973,21977],{"className":21974,"code":21975,"language":21976,"meta":307,"style":307},"language-html shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cp> {{ formatDate('2020-12-25') }} \u003C/p>\n\u003C/template>\n","html",[179,21978,21979,21988,22001],{"__ignoreMap":307},[1736,21980,21981,21983,21986],{"class":1738,"line":1739},[1736,21982,6657],{"class":1912},[1736,21984,21985],{"class":6696},"template",[1736,21987,6663],{"class":1912},[1736,21989,21990,21992,21994,21997,21999],{"class":1738,"line":748},[1736,21991,7020],{"class":1912},[1736,21993,11],{"class":6696},[1736,21995,21996],{"class":1912},"> {{ formatDate('2020-12-25') }} \u003C/",[1736,21998,11],{"class":6696},[1736,22000,6663],{"class":1912},[1736,22002,22003,22005,22007],{"class":1738,"line":756},[1736,22004,8105],{"class":1912},[1736,22006,21985],{"class":6696},[1736,22008,6663],{"class":1912},[11,22010,22011,22012],{},"Result: ",[58,22013,22014],{},"December 25, 2021",[11,22016,22017],{},"We can also use different options. Perhaps we want to show the day of the week. We can do this by adding in the weekday.",[299,22019,22021],{"className":4894,"code":22020,"language":4896,"meta":307,"style":307},"formatDateDay(date) {\n  const options = {  weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }\n  return new Date(date).toLocaleDateString('en-us', options)\n},\n",[179,22022,22023,22031,22059,22078],{"__ignoreMap":307},[1736,22024,22025,22028],{"class":1738,"line":1739},[1736,22026,22027],{"class":2674},"formatDateDay",[1736,22029,22030],{"class":1912},"(date) {\n",[1736,22032,22033,22035,22037,22039,22042,22044,22047,22049,22051,22053,22055,22057],{"class":1738,"line":748},[1736,22034,7824],{"class":4866},[1736,22036,21900],{"class":1918},[1736,22038,4911],{"class":4866},[1736,22040,22041],{"class":1912}," {  weekday: ",[1736,22043,21914],{"class":1935},[1736,22045,22046],{"class":1912},", year: ",[1736,22048,21908],{"class":1935},[1736,22050,21911],{"class":1912},[1736,22052,21914],{"class":1935},[1736,22054,21917],{"class":1912},[1736,22056,21908],{"class":1935},[1736,22058,21922],{"class":1912},[1736,22060,22061,22063,22065,22067,22069,22071,22073,22076],{"class":1738,"line":756},[1736,22062,6685],{"class":4866},[1736,22064,15674],{"class":4866},[1736,22066,16631],{"class":2674},[1736,22068,21933],{"class":1912},[1736,22070,21936],{"class":2674},[1736,22072,7751],{"class":1912},[1736,22074,22075],{"class":1935},"'en-us'",[1736,22077,21944],{"class":1912},[1736,22079,22080],{"class":1738,"line":1755},[1736,22081,16366],{"class":1912},[11,22083,22011,22084],{},[58,22085,22086],{},"Friday, October 9, 2020",[23,22088,22090],{"id":22089},"different-locales","Different Locales",[11,22092,22093],{},"And we can also pass in different locales so we get the date in the right order which is especially useful for when working with UK v US date formatting.",[299,22095,22097],{"className":4894,"code":22096,"language":4896,"meta":307,"style":307},"formatDateEN(date) {\n  const options = { year: 'numeric', month: 'numeric', day: 'numeric' }\n  return new Date(date).toLocaleDateString('en-GB', options)\n},\n",[179,22098,22099,22106,22128,22147],{"__ignoreMap":307},[1736,22100,22101,22104],{"class":1738,"line":1739},[1736,22102,22103],{"class":2674},"formatDateEN",[1736,22105,22030],{"class":1912},[1736,22107,22108,22110,22112,22114,22116,22118,22120,22122,22124,22126],{"class":1738,"line":748},[1736,22109,7824],{"class":4866},[1736,22111,21900],{"class":1918},[1736,22113,4911],{"class":4866},[1736,22115,21905],{"class":1912},[1736,22117,21908],{"class":1935},[1736,22119,21911],{"class":1912},[1736,22121,21908],{"class":1935},[1736,22123,21917],{"class":1912},[1736,22125,21908],{"class":1935},[1736,22127,21922],{"class":1912},[1736,22129,22130,22132,22134,22136,22138,22140,22142,22145],{"class":1738,"line":756},[1736,22131,6685],{"class":4866},[1736,22133,15674],{"class":4866},[1736,22135,16631],{"class":2674},[1736,22137,21933],{"class":1912},[1736,22139,21936],{"class":2674},[1736,22141,7751],{"class":1912},[1736,22143,22144],{"class":1935},"'en-GB'",[1736,22146,21944],{"class":1912},[1736,22148,22149],{"class":1738,"line":1755},[1736,22150,16366],{"class":1912},[11,22152,22011,22153],{},[58,22154,22155],{},"25/12/2020",[299,22157,22159],{"className":4894,"code":22158,"language":4896,"meta":307,"style":307},"formatDateUS(date) {\n  const options = { year: 'numeric', month: 'numeric', day: 'numeric' }\n  return new Date(date).toLocaleDateString('en-US', options)\n},\n",[179,22160,22161,22168,22190,22209],{"__ignoreMap":307},[1736,22162,22163,22166],{"class":1738,"line":1739},[1736,22164,22165],{"class":2674},"formatDateUS",[1736,22167,22030],{"class":1912},[1736,22169,22170,22172,22174,22176,22178,22180,22182,22184,22186,22188],{"class":1738,"line":748},[1736,22171,7824],{"class":4866},[1736,22173,21900],{"class":1918},[1736,22175,4911],{"class":4866},[1736,22177,21905],{"class":1912},[1736,22179,21908],{"class":1935},[1736,22181,21911],{"class":1912},[1736,22183,21908],{"class":1935},[1736,22185,21917],{"class":1912},[1736,22187,21908],{"class":1935},[1736,22189,21922],{"class":1912},[1736,22191,22192,22194,22196,22198,22200,22202,22204,22207],{"class":1738,"line":756},[1736,22193,6685],{"class":4866},[1736,22195,15674],{"class":4866},[1736,22197,16631],{"class":2674},[1736,22199,21933],{"class":1912},[1736,22201,21936],{"class":2674},[1736,22203,7751],{"class":1912},[1736,22205,22206],{"class":1935},"'en-US'",[1736,22208,21944],{"class":1912},[1736,22210,22211],{"class":1738,"line":1755},[1736,22212,16366],{"class":1912},[11,22214,22011,22215],{},[58,22216,22217],{},"12/25/2020",[11,22219,22220],{},"And of course we can also change the format to show the day and month in a different language.",[299,22222,22224],{"className":4894,"code":22223,"language":4896,"meta":307,"style":307},"formatDateDayEs(date) {\n  const options = {  weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }\n  return new Date(date).toLocaleDateString('es', options)\n},\n",[179,22225,22226,22233,22259,22278],{"__ignoreMap":307},[1736,22227,22228,22231],{"class":1738,"line":1739},[1736,22229,22230],{"class":2674},"formatDateDayEs",[1736,22232,22030],{"class":1912},[1736,22234,22235,22237,22239,22241,22243,22245,22247,22249,22251,22253,22255,22257],{"class":1738,"line":748},[1736,22236,7824],{"class":4866},[1736,22238,21900],{"class":1918},[1736,22240,4911],{"class":4866},[1736,22242,22041],{"class":1912},[1736,22244,21914],{"class":1935},[1736,22246,22046],{"class":1912},[1736,22248,21908],{"class":1935},[1736,22250,21911],{"class":1912},[1736,22252,21914],{"class":1935},[1736,22254,21917],{"class":1912},[1736,22256,21908],{"class":1935},[1736,22258,21922],{"class":1912},[1736,22260,22261,22263,22265,22267,22269,22271,22273,22276],{"class":1738,"line":756},[1736,22262,6685],{"class":4866},[1736,22264,15674],{"class":4866},[1736,22266,16631],{"class":2674},[1736,22268,21933],{"class":1912},[1736,22270,21936],{"class":2674},[1736,22272,7751],{"class":1912},[1736,22274,22275],{"class":1935},"'es'",[1736,22277,21944],{"class":1912},[1736,22279,22280],{"class":1738,"line":1755},[1736,22281,16366],{"class":1912},[11,22283,22011,22284],{},[58,22285,22286],{},"viernes, 25 de diciembre de 2020",[23,22288,22290],{"id":22289},"example","Example",[70,22292,22293,22303],{},[73,22294,22295,22302],{},[58,22296,22297],{},[15,22298,22301],{"href":22299,"rel":22300},"https://codepen.io/debs-obrien/pen/oNLgJda",[19],"See my CodePen"," to play around with the dates.",[73,22304,22305,22312],{},[58,22306,22307],{},[15,22308,22311],{"href":22309,"rel":22310},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString",[19],"See the Mozilla Docs"," for more info.",[2011,22314,22315],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":22317},[22318,22319,22320],{"id":21967,"depth":748,"text":21968},{"id":22089,"depth":748,"text":22090},{"id":22289,"depth":748,"text":22290},"2020-10-09","How to format a date in JavaScript without using any libraries so you easily display dates in your Vue or Nuxt application","photo-1595437037073-edcf5af767dd?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/date-formatting",{"title":21838,"description":22322},"blog/date-formatting",[5637],"mVH_xObi2-wW6Eyv2gilJ8PEiksbjfHgV0wVwdYgwn0",{"id":22331,"title":22332,"body":22333,"canonical":22523,"date":22524,"description":22525,"extension":786,"featured":787,"image":788,"meta":22526,"navigation":790,"ogimage":788,"path":22527,"provider":788,"published":790,"seo":22528,"stem":22529,"tags":22530,"url":788,"__hash__":22531},"blog/blog/debugging-my-zsh-config-with-goose-and-why-agentic-ai-actually-helped.md","Debugging My Zsh Config With Goose (and Why Agentic AI Actually Helped)",{"type":8,"value":22334,"toc":22513},[22335,22338,22341,22344,22347,22351,22354,22357,22360,22363,22367,22378,22381,22387,22397,22400,22404,22411,22414,22417,22420,22428,22431,22434,22440,22449,22452,22455,22458,22461,22464,22467,22470,22474,22477,22480,22483,22486,22489,22492,22495,22498],[11,22336,22337],{},"I've been playing around with a few things recently and wanted to share a real experience that genuinely surprised me.",[11,22339,22340],{},"You might have seen the news from the Linux Foundation announcing the formation of the Agentic AI Foundation. As part of that, a few projects were donated into the foundation, including the Model Context Protocol (MCP), Agents.md, and Goose.",[11,22342,22343],{},"I'm guessing a lot of people haven't heard of Goose yet. I hadn't either until recently, so I figured I'd dig into it and see what it's actually about.",[11,22345,22346],{},"If you want the full announcement, you can read it on the Linux Foundation website. This post isn't about the announcement though — it's about what happened when I tried to use Goose for real.",[23,22348,22350],{"id":22349},"what-is-goose","What is Goose?",[11,22352,22353],{},"Goose was released in early 2025, so it's still very new. It's open source (which I love), and it's a local-first AI agent framework. It combines language models with extensible tools like MCP so it can actually do things, not just talk about them.",[11,22355,22356],{},"There's both a desktop app and a CLI. I'm much more of a desktop app person than a CLI person, but everything seems to be going CLI these days, so I decided to give the Goose CLI a try.",[11,22358,22359],{},"The docs live at block.github.io, and I did start reading them. Like most people, I got through the first bit and then thought, \"I'll figure it out as I go.\"",[11,22361,22362],{},"That decision led me directly into a very familiar kind of developer pain.",[23,22364,22366],{"id":22365},"the-problem-goose-wouldnt-run","The Problem: Goose Wouldn't Run",[11,22368,22369,22370,22373,22374,22377],{},"I installed the desktop app first and then followed the docs to install the CLI. The instructions said that after updating my ",[179,22371,22372],{},".zshrc",", I should be able to run the ",[179,22375,22376],{},"goose"," command.",[11,22379,22380],{},"I couldn't. No matter what I did, I kept getting:",[299,22382,22385],{"className":22383,"code":22384,"language":304},[302],"zsh: command not found: goose\n",[179,22386,22384],{"__ignoreMap":307},[11,22388,22389,22390,22392,22393,22396],{},"I sourced my ",[179,22391,22372],{},". I ran ",[179,22394,22395],{},"goose --help",". Nothing worked. The agent kept telling me everything was done correctly, and I kept replying with some version of, \"No, it's not working.\"",[11,22398,22399],{},"This is where it got interesting.",[23,22401,22403],{"id":22402},"goose-didnt-argue-it-investigated","Goose Didn't Argue — It Investigated",[11,22405,22406,22407,22410],{},"Instead of looping on generic advice, Goose pointed out something important: terminal config changes are only picked up when a new session starts. That's something I ",[133,22408,22409],{},"always"," forget.",[11,22412,22413],{},"So I closed the terminal, opened a new one, and tried again.",[11,22415,22416],{},"Still broken.",[11,22418,22419],{},"At this point, Goose acknowledged that something wasn't right. It explained that if sourcing the file and restarting the terminal didn't work, then one of two things was probably happening:",[70,22421,22422,22425],{},[73,22423,22424],{},"The change didn't save correctly",[73,22426,22427],{},"Another startup config was overriding the PATH",[11,22429,22430],{},"Honestly, that explanation alone would normally make me sigh and prepare to lose an hour.",[11,22432,22433],{},"Instead, Goose suggested checking the file directly.",[23,22435,22437,22438],{"id":22436},"letting-an-ai-read-my-zshrc","Letting an AI Read My ",[179,22439,22372],{},[11,22441,22442,22443,22445,22446,22448],{},"Goose used a tool to open my ",[179,22444,22372],{}," and read it. I didn't need to install this tool. Reading ",[179,22447,22372],{}," files is not something I do often and I definitely don't enjoy debugging them.",[11,22450,22451],{},"Goose scanned through all the usual stuff, pnpm, bun, nvm, PATH exports and then immediately spotted the problem.",[11,22453,22454],{},"When it had added the Goose PATH export earlier, it didn't include a newline. That meant the new export was stuck onto the end of the previous line, creating a syntax error.",[11,22456,22457],{},"I wouldn't have noticed that quickly. I was looking at the file and just seeing noise.",[11,22459,22460],{},"Goose explained exactly what went wrong, showed me the broken line, and explained that it should really be two separate lines.",[11,22462,22463],{},"Then it fixed it. It replaced the bad line with two clean, correctly formatted lines and even added a comment to make it clearer for the future.",[11,22465,22466],{},"After that, Goose asked me to restart my terminal one more time. This time, when I typed goose, the command worked. I could see all the available commands. Sessions, MCP servers, bundled tools, everything was there.",[11,22468,22469],{},"At that point, I just sat back for a second. Not because this was some massive, complex bug, but because this is exactly the kind of small, annoying issue that can completely derail your flow.",[23,22471,22473],{"id":22472},"why-this-actually-matters","Why This Actually Matters",[11,22475,22476],{},"If I had debugged this myself, I would have figured it out eventually. But it would have taken time, frustration, and a lot of trial and error. Instead, the agent noticed something was off. It inspected a real config file and identified a subtle syntax error, fixed it safely and then it explained what happened.",[11,22478,22479],{},"That's the difference between AI that answers questions and AI that actually helps you get unstuck.",[11,22481,22482],{},"I also didn't know Goose could edit files like this. Seeing it work through the problem step by step, without pretending everything was fine when it wasn't, made a big difference.",[11,22484,22485],{},"We don't have the answers to everything as developers. That's normal. What is changing is how quickly we can get unblocked.",[11,22487,22488],{},"If something doesn't feel right, push back. Say it's not working. Let the agent iterate. Let it re-check assumptions. That's where this starts to become genuinely useful.",[11,22490,22491],{},"If you're curious about Goose, it's worth a look. And even if you're not, this kind of experience is a good reminder that using AI well isn't about shortcut, it's about reducing unnecessary friction.",[11,22493,22494],{},"That's it. Have fun experimenting with AI.",[138,22496,22497],{"id":5705},"Useful Links:",[70,22499,22500,22506],{},[73,22501,22502],{},[15,22503,20],{"href":22504,"rel":22505},"https://block.github.io/goose/docs/getting-started/installation/",[19],[73,22507,22508],{},[15,22509,22512],{"href":22510,"rel":22511},"https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation",[19],"Linux Foudation",{"title":307,"searchDepth":748,"depth":748,"links":22514},[22515,22516,22517,22518,22520],{"id":22349,"depth":748,"text":22350},{"id":22365,"depth":748,"text":22366},{"id":22402,"depth":748,"text":22403},{"id":22436,"depth":748,"text":22519},"Letting an AI Read My .zshrc",{"id":22472,"depth":748,"text":22473,"children":22521},[22522],{"id":5705,"depth":756,"text":22497},"https://dev.to/debs_obrien/debugging-my-zsh-config-with-goose-and-why-agentic-ai-actually-helped-1noh","2025-12-15","I've been playing around with a few things recently and wanted to share a real experience that genuinely surprised me. When Goose CLI wouldn't run, the agent investigated my .zshrc file and found a subtle syntax error that would have taken me much longer to debug manually.",{},"/blog/debugging-my-zsh-config-with-goose-and-why-agentic-ai-actually-helped",{"title":22332,"description":22525},"blog/debugging-my-zsh-config-with-goose-and-why-agentic-ai-actually-helped",[795,796],"QDTmcblgFZA5mwzjK333TZyXeNGZOYPmsf4VswBeGS4",{"id":22533,"title":22534,"body":22535,"canonical":788,"date":22768,"description":22769,"extension":786,"featured":787,"image":22770,"meta":22771,"navigation":790,"ogimage":788,"path":22773,"provider":3460,"published":790,"seo":22774,"stem":22775,"tags":22776,"url":788,"__hash__":22777},"blog/blog/debugging-tests-in-playwright.md","Debugging Tests in Playwright",{"type":8,"value":22536,"toc":22758},[22537,22540,22549,22552,22556,22560,22563,22575,22579,22582,22597,22600,22617,22621,22624,22632,22635,22641,22650,22653,22681,22684,22688,22691,22694,22702,22705,22707,22718,22720,22755],[11,22538,22539],{},"Playwright scripts work with existing debugging tools, like Node.js debuggers and browser developer tools. Playwright also introduces new debugging features for browser automation.",[11,22541,22542,22543,22548],{},"If you are new to testing with Playwright, you might want to check out my previous post ",[15,22544,22547],{"href":22545,"rel":22546},"https://debbie.codes/blog/getting-started-with-playwright-testing",[19],"Getting Started with Playwright"," to get a better understanding of how Playwright works and how to easily get started with this amazing tool.",[11,22550,22551],{},"Let's take a look at some of the ways you can debug your tests should they fail.",[23,22553,22555],{"id":22554},"running-tests","Running Tests",[138,22557,22559],{"id":22558},"vs-code","VS Code",[11,22561,22562],{},"Once you have generated your code using codegen and pasted it into your test file in VS Code you can then run the test by pressing the green triangle next to the line where your test starts. Playwright will run through each step of the test and show you that the test passed.",[22564,22565,5302,22566,5302,22570,22574],"video",{"width":6371,"height":6371,"controls":790},[5304,22567],{"src":22568,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/f_auto,q_auto/v1648208896/debbie.codes/blog/2022/running-tests_l4uye7.mp4","video/mp4",[5304,22571],{"src":22572,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/f_auto,q_auto/v1648208896/debbie.codes/blog/2022/running-tests_l4uye7.ogg","video/ogg","\nYour browser does not support the video tag.\n",[138,22576,22578],{"id":22577},"cli","CLI",[11,22580,22581],{},"Alternatively you can run the tests using the following command which will run the test in headed mode meaning you will see the output of the test only in the terminal.",[299,22583,22585],{"className":2665,"code":22584,"language":2667,"meta":307,"style":307},"npx playwright test\n",[179,22586,22587],{"__ignoreMap":307},[1736,22588,22589,22591,22594],{"class":1738,"line":1739},[1736,22590,2675],{"class":2674},[1736,22592,22593],{"class":1935}," playwright",[1736,22595,22596],{"class":1935}," test\n",[11,22598,22599],{},"If you want to run the test in headed mode you can add the '--headed' flag to the command. This will open a browser window where you will see Playwright run through each step of the test. Be wared though as tests are super fast so it will literally flash before your eyes. Later we will look at how to stop the test so the browser window stays open.",[299,22601,22603],{"className":2665,"code":22602,"language":2667,"meta":307,"style":307},"npx playwright test --headed\n",[179,22604,22605],{"__ignoreMap":307},[1736,22606,22607,22609,22611,22614],{"class":1738,"line":1739},[1736,22608,2675],{"class":2674},[1736,22610,22593],{"class":1935},[1736,22612,22613],{"class":1935}," test",[1736,22615,22616],{"class":1918}," --headed\n",[23,22618,22620],{"id":22619},"breaking-tests","Breaking Tests",[11,22622,22623],{},"As we have written a perfect test it is no fun, as the test passes. Lets make the test fail so we can debug it. In this example I will change the locator text from Women to Men. When creating this example I expected the test to break on line 9 which should now look for an incorrect route but my test actually broke before it even got to that line. Take a look and see for yourself.",[22564,22625,5302,22626,5302,22629,22574],{"width":6371,"height":6371,"controls":790},[5304,22627],{"src":22628,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648210220/debbie.codes/blog/2022/failing-tests_nz5bkj.mp4",[5304,22630],{"src":22631,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648210220/debbie.codes/blog/2022/failing-tests_nz5bkj.ogg",[11,22633,22634],{},"The error messaging from VS Code is very clear and it is telling us exactly why it broke.",[11,22636,22637],{},[121,22638],{"alt":22639,"src":22640},"error message for broken tests","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648217584/debbie.codes/blog/2022/running-tests_2x_twjt1c.png",[11,22642,22643,22644,22649],{},"The reason is strict mode, which we looked at in a ",[15,22645,22648],{"href":22646,"rel":22647},"https://debbie.codes/blog/testing-iframes-with-playwright",[19],"previous post",". With strict mode you can only have one instance and as the word Men can also be found in the word Women. Playwright doesn't know which one to select and so fails.",[11,22651,22652],{},"VS Code gives us two options to solve this which are, to use the word Women, or to use selectors and select the first child of the element that has the text Men. In our case the Text men is the first instance of the nav bar and therefore it will select that.",[299,22654,22656],{"className":8734,"code":22655,"language":8736,"meta":307,"style":307},"await page.locator('Text=Men >> nth=0').click()\n",[179,22657,22658],{"__ignoreMap":307},[1736,22659,22660,22663,22666,22669,22671,22674,22676,22678],{"class":1738,"line":1739},[1736,22661,22662],{"class":4866},"await",[1736,22664,22665],{"class":1912}," page.",[1736,22667,22668],{"class":2674},"locator",[1736,22670,7751],{"class":1912},[1736,22672,22673],{"class":1935},"'Text=Men >> nth=0'",[1736,22675,911],{"class":1912},[1736,22677,10804],{"class":2674},[1736,22679,22680],{"class":1912},"()\n",[11,22682,22683],{},"Once we have added this we can rerun the test and now it will fail on the url as we expected.",[23,22685,22687],{"id":22686},"debugging-tests","Debugging Tests",[11,22689,22690],{},"If we want to visualize what is going on when the test fails we can add a breakpoint right in VS Code itself. You can do this by clicking next to the line where you want to add the breakpoint. You should see a red dot once the breakpoint is added. Then right click on the green triangle and choose 'run in debug mode'. This will open a browser window and show us what is happening at that breakpoint.",[11,22692,22693],{},"In this example it will show us that the test has issues with the text 'Men' as it appears in both 'Men' and 'Women'. If we add the word 'hello' in our editor we won't see anything highlighted in the browser as there is no word 'hello' on our page. However if we change it to the word 'Children' then we will see in our browser that Children is selected.",[22564,22695,5302,22696,5302,22699,22574],{"width":6371,"height":6371,"controls":790},[5304,22697],{"src":22698,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648211390/debbie.codes/blog/2022/debugging-tests_mze1rs.mp4",[5304,22700],{"src":22701,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648211390/debbie.codes/blog/2022/debugging-tests_mze1rs.ogg",[11,22703,22704],{},"You can use the buttons in VS Code to step through the test, replay the test etc. Don't forget to set a breakpoint when running in debug mode otherwise it will quickly open the browser window and close it again as there is nothing to debug.",[23,22706,3294],{"id":3293},[11,22708,22709,22710,22714,22715,10763],{},"Being able to interact directly with your code and see the selectors highlighted is pretty cool and extremely useful for debugging. It also makes debugging fun. Check out the ",[15,22711,22713],{"href":22712},"(https://playwright.dev/docs/debug)","debugging docs"," for more options of debugging including the ",[179,22716,22717],{},"page.pause()",[23,22719,5706],{"id":5705},[70,22721,22722,22729,22736,22743,22750],{},[73,22723,22724],{},[15,22725,22728],{"href":22726,"rel":22727},"https://playwright.dev/",[19],"Playwright docs",[73,22730,22731],{},[15,22732,22735],{"href":22733,"rel":22734},"https://playwright.dev/docs/cli#generate-code",[19],"Codegen",[73,22737,22738],{},[15,22739,22742],{"href":22740,"rel":22741},"https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright",[19],"VS Code Extension",[73,22744,22745],{},[15,22746,22749],{"href":22747,"rel":22748},"https://playwright.dev/docs/debug",[19],"Debugging in Playwright",[73,22751,22752],{},[15,22753,22547],{"href":22545,"rel":22754},[19],[2011,22756,22757],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":307,"searchDepth":748,"depth":748,"links":22759},[22760,22764,22765,22766,22767],{"id":22554,"depth":748,"text":22555,"children":22761},[22762,22763],{"id":22558,"depth":756,"text":22559},{"id":22577,"depth":756,"text":22578},{"id":22619,"depth":748,"text":22620},{"id":22686,"depth":748,"text":22687},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2022-03-25","Let's take a look at some of the ways you can debug your tests in Playwright should they fail. Playwright scripts work with existing debugging tools, like Node.js debuggers and browser developer tools. Playwright also introduces new debugging features for browser automation.","v1648217584/debbie.codes/blog/2022/running-tests_2x_twjt1c.png",{"ogImage":22772,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648217584/debbie.codes/blog/2022/running-tests_2x_twjt1c.png","/blog/debugging-tests-in-playwright",{"title":22534,"description":22769},"blog/debugging-tests-in-playwright",[1411,1412],"7irwzJpfGhHw5JLmrob_81RSqbTqlSVX97x2obozB18",{"id":22779,"title":22780,"body":22781,"canonical":788,"date":22994,"description":22995,"extension":786,"featured":787,"image":22996,"meta":22997,"navigation":790,"ogimage":788,"path":22999,"provider":3460,"published":790,"seo":23000,"stem":23001,"tags":23002,"url":788,"__hash__":23003},"blog/blog/delivering-a-talk.md","Delivering a Talk",{"type":8,"value":22782,"toc":22980},[22783,22786,22789,22793,22796,22799,22802,22806,22809,22814,22817,22823,22826,22829,22833,22836,22839,22842,22845,22849,22852,22857,22861,22864,22873,22876,22880,22883,22886,22890,22893,22896,22901,22904,22908,22911,22915,22918,22921,22924,22927,22930,22933,22935,22938,22944,22948],[11,22784,22785],{},"First of all let me start by giving you a little background to me and how I started to become a speaker. First thing to take note is do not compare yourself to others cause it is most likely not a fair comparison. Many people ask me how I have such good stage presence and the answer is pretty simple.",[11,22787,22788],{},"I spent many years in drama school and theatre groups. As a teenager I was on the stage giving performances in front of hundreds of people. I learnt how to act, how to use my voice, how to read the audience and how to own the stage. This experience I take with me to all my talks.",[23,22790,22792],{"id":22791},"acting-and-speaking-is-not-the-same","Acting and Speaking is not the same",[11,22794,22795],{},"However being an actress in the theatre and being a tech speaker are two totally different worlds. Let me take you back to my secondary school where I was asked to stand up in front of the class and read out my homework, a review of a tv program. I refused.",[11,22797,22798],{},"I told the teacher I had not done my homework. Thing is I was the kinda student who always did their homework so he didn't believe me. But he couldn't make me stand up in front of the class. So at the end of the class he held me back and I will never forget his words. He said I know you have done your homework. What I don't understand is why you won't come and read it out in front of 30 people. I was at the theatre last night and saw you on the stage in front of hundreds of people and you were amazing. So what's happening here.",[11,22800,22801],{},"My answer was simply. When I am on stage I am someone else. I am comfortable on the stage being a character cause it doesn't matter if people like or dislike me or if people judge me for who I am because it is not me. But when I get up in front of the class to read my homework, now they are judging me. I am exposed. I have no one to hide behind and I don't want people to dislike me. Basically I was terrified of being me.",[23,22803,22805],{"id":22804},"my-first-conference","My First Conference",[11,22807,22808],{},"Now fast forward many few years and I am at a conference and I see some talks on Imposter syndrome. It really resonates with me and I go up afterwards to speak to one of the speakers and I tell her how much I love the stage but I am terrified of giving a tech talk to a room of experts.",[1713,22810,22811],{},[11,22812,22813],{},"I said it felt like I would be teaching a Taekwondo class to a room of masters and that wouldn't make sense. They all know more than me.",[11,22815,22816],{},"Now this is where my first mistake comes from. Who said the room was full of experts. Who decided that everyone had more knowledge than me. That's the imposter syndrome kicking in.",[11,22818,22819],{},[121,22820],{"alt":22821,"src":22822},"me on stage","https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673858048/debbie.codes/blog/2023/my-first-conference_gheazh.jpg",[11,22824,22825],{},"Truth is we have no idea of what level everyone in the audience is but when they come to watch a talk they have decided to sit in the room and watch that talk and everyone will have a different knowledge range and more than likely everyone will learn something from your talk because the way I tell a story and the way someone else tells a story is completely different and we need people to tell things in different ways.",[11,22827,22828],{},"But what about all those people sitting judging you? Well at a conference people are not there to judge. People want you to succeed. If its your first talk and you are nervous just tell them, you will be surprised at the level of support you will get because everyone in the audience knows how hard it is to get up on a stage and give a talk and most of them won't ever dare try.",[23,22830,22832],{"id":22831},"overcoming-the-imposter","Overcoming the Imposter",[11,22834,22835],{},"So really the only thing that is stopping you from giving a talk is your own fears and imposter syndrome. When it comes to fear the best way to look at it is, if it's scary then you should totally be doing it. Doing scary stuff helps you grow, you step outside of your comfort zone. So really you should walk with fear not against it. As for imposter syndrome we all suffer from it one way or another.",[11,22837,22838],{},"A few tips I have to overcome it. I hang signs all over my wall saying things like \"Believe in yourself\", \"you are amazing just the way you are\" etc. and anytime I feel like I can't do something I read these signs.",[11,22840,22841],{},"I also programmed Alexa so that when I say good morning she tells me Debbie you are amazing. Before a talk I literally coach myself and say, Debbie you got this, you are going to do amazing, you are going to walk out there and above all you are going to enjoy every second of being on that stage.",[11,22843,22844],{},"I get mega nervous before any talk. No matter how many years of experience or talks I have done. My body just gets nervous and I need to use the bathroom so many times before they wire me up. But talking to myself and telling myself I can do this really helps me and then I use those nerves as energy once I get on the stage.",[23,22846,22848],{"id":22847},"on-the-stage","On the Stage",[11,22850,22851],{},"Now the first thing I do when I get on the stage is I talk about me. My first slide is about me. I have a picture of me doing sport and I tell people what sports I do and I spend a good few minutes on who I am. I do this for various reasons. The audience get to know who I am helping to connect with me better. And I don't need to rehearse anything to talk about me, I know this material so it helps stabilize my nerves so when I do go into the tech stuff I feel more in control and more comfortable with myself and the audience.",[11,22853,22854],{},[121,22855],{"alt":22821,"src":22856},"https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673858048/debbie.codes/blog/2023/speaking-on-stage_jco3ft.jpg",[23,22858,22860],{"id":22859},"connecting-with-the-audience","Connecting with the Audience",[11,22862,22863],{},"So how do you connect with the audience?\nThis can take time and practice but I asked a good speaker friend, Kevlin Henney, to one day watch my talk and give me feedback on how I can improve as a speaker. He took notes and it was super useful and one thing he said was you need to connect with the audience more.",[11,22865,22866,22867,22872],{},"I guess its like the difference between the cinema and the theatre. In the cinema the audience might laugh but there is no real connection to the characters, they are very far away. Whereas in the theatre the actors can interact with the audience and it creates a whole different experience. Kevlin suggested that I watch a specific speaker as he was very good at delivering talks. That speaker is ",[15,22868,22871],{"href":22869,"rel":22870},"https://youtu.be/-_Qjhqeq8tU?si=2ZFglBGJvUT0nC6c",[19],"Vitaly Friedman from Smashing Magazine"," incase you want to watch any of his talks.",[11,22874,22875],{},"So what was he doing that I was not doing? He was talking to the audience. He was asking them questions. He was entertaining them. He was asking them what they expected to see or happen. He was making them laugh. He was connecting with the audience and he had them in his hands. I highly encourage you to watch one of his talks.",[138,22877,22879],{"id":22878},"stage-presence","Stage Presence",[11,22881,22882],{},"Ok lets take it back a step to the stage presence. I personally like to walk the stage. That is mainly cause I am no good at keeping still but also because sometimes you are on one side of a big stage and the audience that is on the other side can feel very left out and disconnected. Eye contact is fine but I am going to tell you how I do it. I literally walk the stage from one end to the other and stop and say a few things then go to the middle then to the other end.",[11,22884,22885],{},"I visualize a triangle and tend to look at those points of the triangle as I deliver my talk. This makes it feel like I am looking at everyone or at least trying to connect with the whole audience. The next step would be to pick a few people in the audience and make eye contact with them and really play off of their reactions. This takes a bit more practice of course and can be really scary at first but you can always start off my looking and connecting with the other speakers.",[138,22887,22889],{"id":22888},"talking-to-the-audience","Talking to the Audience",[11,22891,22892],{},"So how do you connect with the audience? First of all say hello to them. Ask them how they are doing. Then ask them questions. The audience love to be interacted with and they will always answer you whether it be putting up your hand or shouting out yes or no. It can be simple questions such as \"are you testing your applications\" or are you using xyz in production. This also gives you a better idea of who your audience is or what level they have, making it easier to tailor your talk ever so slightly to cover or not cover certain aspects.",[11,22894,22895],{},"Once you start presenting your slides it is so easy to go into slide mode and just talk for 30 minutes without breathing and forgetting completely that you have a few hundred people looking at you. This is where it is important to think of how you can connect back to the audience. It can be as simple as adding a funny gif to make them laugh and get a reaction from them or asking them if they liked the demo or what they thought about a feature which will encourage them to clap and that always gives you more energy.",[11,22897,22898],{},[121,22899],{"alt":22821,"src":22900},"https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673858048/debbie.codes/blog/2023/me-on-stage_ttc3ho.jpg",[11,22902,22903],{},"As you get more confident you can start to ask questions such as what do you think is going to happen next or why is something working a certain way. People will always shout out an answer, and they love it when they are right, so use that to your advantage. The more you engage the audience during the talk the more they will enjoy it and remember it.",[138,22905,22907],{"id":22906},"bring-fun-into-your-talk","Bring Fun into your Talk",[11,22909,22910],{},"As for the technical content you don't want it to be too technical. You want people to learn something and walk away with some new found knowledge but you don't want to wreck their heads and make them feel like they have just sat through an extensive exam. Even high level technical talks can have funny moments in them so try to bring some fun into your talks as much as possible.",[23,22912,22914],{"id":22913},"online-talks","Online Talks",[11,22916,22917],{},"When it comes to giving a talk online it is a little different indeed. You can't walk around and most of the time you have no audience in front of you. Sometimes it really can feel like you are talking to a brick wall. A couple of things that can help:",[11,22919,22920],{},"If it is a pre-recorded talk ask someone to turn on their camera so that you feel like you are actually talking to someone and can see their reactions.",[11,22922,22923],{},"If you have to record it yourself and send it in then you can either ask some friends to join the call while you record or leave your camera visible showing you so then you are talking to yourself which is kinda weird but can help you keep looking at the camera if you place the video of yourself right under the camera.",[11,22925,22926],{},"Online talks take up so much more energy than in person talks. It is much more like acting as you can't use the audience to bounce that energy off of and you can't ask questions either so you just have to put lots of energy into it.",[11,22928,22929],{},"Make sure your slides have some nice visuals or funny gifs and you can still talk to the audience, pretend they are there. Ask things like what do you think, cool eh, Yeh I knew you would love that. Pure acting I know. But it works.",[11,22931,22932],{},"If you do get to give a talk to a live online audience then I like to leave the chat open and tell people to throw things in the chat so I can see that they are there. It can also help but it can also be distracting so depends on you, how lively the chat is and the type of talk you are doing. Demo heavy or not for example. And make sure some of them turn cameras on so you can see some reactions.",[23,22934,3294],{"id":3293},[11,22936,22937],{},"That really is about it. Giving talks can be scary but really it is so much fun. Sharing your knowledge with others and helping them learn something new is so rewarding. Remember everyone is different and has a different story to tell so don't be afraid to get up on that stage and tell your story of how you learnt something or how you solved a problem.",[11,22939,22940],{},[121,22941],{"alt":22942,"src":22943},"person leaping over a mountain","https://res.cloudinary.com/debsobrien/image/upload/w_1000,ar_16:9,c_fill,g_auto,e_sharpen/v1673858048/debbie.codes/blog/2023/leaping-over-mountain_i5kxvo.jpg",[23,22945,22947],{"id":22946},"few-things-to-remember","Few things to remember",[70,22949,22950,22953,22956,22959,22962,22965,22968,22971,22974,22977],{},[73,22951,22952],{},"everyone is different and will deliver talks differently\nbe yourself, learn from others but dont try to be them\nconnect with the audience",[73,22954,22955],{},"coach yourself before you go on stage",[73,22957,22958],{},"kick imposter syndrome in the but",[73,22960,22961],{},"remember the audience want you to succeed",[73,22963,22964],{},"nothing is perfect first time",[73,22966,22967],{},"ask others for feedback and how can you improve for next time",[73,22969,22970],{},"watch conference talks and see what you like about how speakers deliver their talks",[73,22972,22973],{},"always practice your talk before going on stage either to an audience, recording yourself and watching it back or just on your own",[73,22975,22976],{},"learn how to control your timings, have extra slides at the end that you can use if you have extra time but don't need to cover if you don't",[73,22978,22979],{},"don't let fear stop you from doing something you want to do",{"title":307,"searchDepth":748,"depth":748,"links":22981},[22982,22983,22984,22985,22986,22991,22992,22993],{"id":22791,"depth":748,"text":22792},{"id":22804,"depth":748,"text":22805},{"id":22831,"depth":748,"text":22832},{"id":22847,"depth":748,"text":22848},{"id":22859,"depth":748,"text":22860,"children":22987},[22988,22989,22990],{"id":22878,"depth":756,"text":22879},{"id":22888,"depth":756,"text":22889},{"id":22906,"depth":756,"text":22907},{"id":22913,"depth":748,"text":22914},{"id":3293,"depth":748,"text":3294},{"id":22946,"depth":748,"text":22947},"2023-10-20","How can we walk on that stage with more confidence, deliver a great talk and not only own the stage but also connect with the audience. It’s not easy, but like everything it does get easier with practice. Let’s go over a few ways you can improve your public speaking skills, either for the big stage or for remote talks, and let’s discuss what is holding you back from becoming an amazing tech speaker.","v1673861872/debbie.codes/blog/2023/vue-amsterdam_i3hjbi.jpg",{"ogImage":22998},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651559990/debbie.codes/blog/2023/vue-amsterdam_i3hjbi.jpg","/blog/delivering-a-talk",{"title":22780,"description":22995},"blog/delivering-a-talk",[3464,20921,5495],"Tq76ah9qH64e7ouEPJoMRk31N12PS3hTZNFZ4fXn4jY",{"id":23005,"title":23006,"body":23007,"canonical":788,"date":23734,"description":23011,"extension":786,"featured":787,"image":23735,"meta":23736,"navigation":790,"ogimage":788,"path":23738,"provider":3460,"published":790,"seo":23739,"stem":23740,"tags":23741,"url":788,"__hash__":23742},"blog/blog/design-tokens-and-theming.md","Design Tokens and Theming",{"type":8,"value":23008,"toc":23719},[23009,23012,23015,23018,23021,23025,23028,23047,23157,23166,23223,23226,23229,23235,23239,23242,23245,23249,23252,23261,23278,23286,23319,23323,23331,23337,23341,23344,23348,23351,23357,23360,23363,23365,23368,23395,23398,23402,23405,23408,23428,23434,23440,23443,23603,23607,23610,23613,23622,23628,23631,23634,23637,23643,23647,23650,23654,23657,23663,23665,23668,23671,23674,23676,23716],[11,23010,23011],{},"As frontend developers we are normally given a design from Figma or similar tools that we then need to turn into code. So where do we start? What are the first steps we should take when converting our design into code?",[11,23013,23014],{},"if you are lucky to work directly with the designers then this is great, but of course sometimes designs come in from an agency and there is not much room for communication with designers, which is a pity. So if you do work directly with your designers how should you proceed?",[11,23016,23017],{},"With Figma designers can design components as symbols, which can then be reused across their designs. They know what components they have previously designed and used and what components need to be designed from scratch. Designers should also have a design system where they use design tokens so that their designs are consistent, using the same color palette or same spacing for example.",[11,23019,23020],{},"But as developers were are just given a completed design that we must then convert into code. We might be tempted to just start building. We have a deadline after all so the sooner we start the better. But if it is a large scale project we might end up with some bigger problems later on that are much harder to solve. But what if we could work much closer to the designers? What if we could use their design tokens and create our design system with those tokens! Bridging the gap between designers and developers is key.",[23,23022,23024],{"id":23023},"so-lets-talk-design-systems","So lets talk design systems",[11,23026,23027],{},"Designers create design tokens for various things such as colors, margins, borders etc. These design tokens can then be exported from Figma or similar tools as JSON objects and used in a theme component that can be then applied to our components. That way everything can be then styled, using CSS in JS or CSS vars, and all our components are ready to accept new tokens or be used with different modes or themes.",[11,23029,23030,23031,23036,23037,23042,23043,23046],{},"In this example I have created a ",[15,23032,23035],{"href":23033,"rel":23034},"https://bit.cloud/learn-bit-react/base-ui/themes/base-theme",[19],"base theme component"," that takes all these ",[15,23038,23041],{"href":23039,"rel":23040},"https://bit.cloud/learn-bit-react/base-ui/themes/base-theme/~code/base-theme-tokens.tsx",[19],"design token JSON files"," and merges them together using ",[179,23044,23045],{},"Object.assign",". By creating them as separate files I can then showcase them all individually. Adding a new design token to the primary colors for example will auto render the token so that the developer can easily see the new color and value.",[299,23048,23050],{"className":8734,"code":23049,"language":8736,"meta":307,"style":307},"export const baseTheme: Partial\u003CThemeSchema> = Object.assign(\n  tokens,\n  fontFamilyTokens,\n  fontSizeTokens,\n  fontWeightTokens,\n  primaryColorTokens,\n  secondaryColorTokens,\n  errorColorTokens,\n  borderRadiusTokens,\n  borderSizeTokens,\n  textColorTokens,\n  backgroundColorTokens,\n  boxShadowTokens,\n  generalColorTokens,\n  headingsSizeTokens\n)\n",[179,23051,23052,23083,23088,23093,23098,23103,23108,23113,23118,23123,23128,23133,23138,23143,23148,23153],{"__ignoreMap":307},[1736,23053,23054,23056,23058,23061,23063,23066,23068,23071,23073,23075,23078,23081],{"class":1738,"line":1739},[1736,23055,6632],{"class":4866},[1736,23057,7002],{"class":4866},[1736,23059,23060],{"class":1918}," baseTheme",[1736,23062,1087],{"class":4866},[1736,23064,23065],{"class":2674}," Partial",[1736,23067,6657],{"class":1912},[1736,23069,23070],{"class":2674},"ThemeSchema",[1736,23072,13932],{"class":1912},[1736,23074,5062],{"class":4866},[1736,23076,23077],{"class":1912}," Object.",[1736,23079,23080],{"class":2674},"assign",[1736,23082,14949],{"class":1912},[1736,23084,23085],{"class":1738,"line":748},[1736,23086,23087],{"class":1912},"  tokens,\n",[1736,23089,23090],{"class":1738,"line":756},[1736,23091,23092],{"class":1912},"  fontFamilyTokens,\n",[1736,23094,23095],{"class":1738,"line":1755},[1736,23096,23097],{"class":1912},"  fontSizeTokens,\n",[1736,23099,23100],{"class":1738,"line":1761},[1736,23101,23102],{"class":1912},"  fontWeightTokens,\n",[1736,23104,23105],{"class":1738,"line":1767},[1736,23106,23107],{"class":1912},"  primaryColorTokens,\n",[1736,23109,23110],{"class":1738,"line":1772},[1736,23111,23112],{"class":1912},"  secondaryColorTokens,\n",[1736,23114,23115],{"class":1738,"line":1778},[1736,23116,23117],{"class":1912},"  errorColorTokens,\n",[1736,23119,23120],{"class":1738,"line":1784},[1736,23121,23122],{"class":1912},"  borderRadiusTokens,\n",[1736,23124,23125],{"class":1738,"line":1790},[1736,23126,23127],{"class":1912},"  borderSizeTokens,\n",[1736,23129,23130],{"class":1738,"line":1796},[1736,23131,23132],{"class":1912},"  textColorTokens,\n",[1736,23134,23135],{"class":1738,"line":2353},[1736,23136,23137],{"class":1912},"  backgroundColorTokens,\n",[1736,23139,23140],{"class":1738,"line":2358},[1736,23141,23142],{"class":1912},"  boxShadowTokens,\n",[1736,23144,23145],{"class":1738,"line":2364},[1736,23146,23147],{"class":1912},"  generalColorTokens,\n",[1736,23149,23150],{"class":1738,"line":2370},[1736,23151,23152],{"class":1912},"  headingsSizeTokens\n",[1736,23154,23155],{"class":1738,"line":2376},[1736,23156,7045],{"class":1912},[11,23158,23159,23160,23165],{},"I did this by creating a ",[15,23161,23164],{"href":23162,"rel":23163},"https://bit.cloud/learn-bit-react/base-ui/figma/design-tokens-viewer",[19],"design token viewer component"," that takes the design token and renders it showing the name and value as well as the CSS variable name. I then created a composition for each design token using the design token viewer component.",[299,23167,23169],{"className":8734,"code":23168,"language":8736,"meta":307,"style":307},"\u003CDesignTokensViewer\n  tokens={borderSizeTokens}\n  additionalStyles={{ border: 'solid black' }}\n  cssKey={'border-width'}\n/>\n",[179,23170,23171,23178,23188,23204,23218],{"__ignoreMap":307},[1736,23172,23173,23175],{"class":1738,"line":1739},[1736,23174,6657],{"class":1912},[1736,23176,23177],{"class":1918},"DesignTokensViewer\n",[1736,23179,23180,23183,23185],{"class":1738,"line":748},[1736,23181,23182],{"class":2674},"  tokens",[1736,23184,5062],{"class":4866},[1736,23186,23187],{"class":1912},"{borderSizeTokens}\n",[1736,23189,23190,23193,23195,23198,23201],{"class":1738,"line":756},[1736,23191,23192],{"class":2674},"  additionalStyles",[1736,23194,5062],{"class":4866},[1736,23196,23197],{"class":1912},"{{ border: ",[1736,23199,23200],{"class":1935},"'solid black'",[1736,23202,23203],{"class":1912}," }}\n",[1736,23205,23206,23209,23211,23213,23216],{"class":1738,"line":1755},[1736,23207,23208],{"class":2674},"  cssKey",[1736,23210,5062],{"class":4866},[1736,23212,7387],{"class":1912},[1736,23214,23215],{"class":1935},"'border-width'",[1736,23217,1976],{"class":1912},[1736,23219,23220],{"class":1738,"line":1761},[1736,23221,23222],{"class":1912},"/>\n",[11,23224,23225],{},"There is of course still a missing connection between the designer and the developer in that if they make a change to the design tokens or add a new one there is manual work involved in then updating the theme component files. Although there are some tools that seem to have this feature I have not personally been able to find a good solution with the tools I am working with. What we tend to do here is give the ownership of the theme component to the designer meaning they can update and add design tokens whenever they need to.",[11,23227,23228],{},"Allowing the designer to own the theme component makes it easier for them to really keep the design system alive and current modifying token values and adding new ones and developers just have to use the theme component to get all these changes and can always see the rendered design tokens that are available to them.",[11,23230,23231],{},[121,23232],{"alt":23233,"src":23234},"design tokens of base theme showing properties and values for colors and borer radius","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648117557/debbie.codes/blog/2022/base-theme_2x_xhbxgc.png",[23,23236,23238],{"id":23237},"the-base-theme-component","The Base Theme Component",[11,23240,23241],{},"The base theme is a component that contains all the design tokens and values for the base theme which can be overridden by any other theme. A theme schema can be created if using typescript. This will ensure our values are correct and using the right type.",[11,23243,23244],{},"Design tokens should be sorted into CSS properties as this makes it easier to view them with the design token viewer. This means we would have a design token file for primary colors, font families, font sizes etc.",[23,23246,23248],{"id":23247},"theme-providers","Theme Providers",[11,23250,23251],{},"in order to use the base theme component in React we need to create a theme provider. This will allow us to provide the theme to all components that are wrapped inside it. The theme provider must be able to read the design tokens and convert them into CSS variables so the design tokens can be used by the component in either CSS in JS or as CSS vars either inline or in a separate stylesheet.",[11,23253,23254,23255,23260],{},"In order to make sure the design tokes are converted to CSS vars I will use the createTheme function from teambits ",[15,23256,23259],{"href":23257,"rel":23258},"https://bit.cloud/learn-bit-react/base-ui/themes/theme-context-provider",[19],"Theme Provider component",". This component manages the converting to CSS vars for me. Feel free to read up more on how it works.",[299,23262,23264],{"className":4894,"code":23263,"language":4896,"meta":307,"style":307},"import { createTheme } from '@teambit/base-react.theme.theme-provider'\n",[179,23265,23266],{"__ignoreMap":307},[1736,23267,23268,23270,23273,23275],{"class":1738,"line":1739},[1736,23269,4996],{"class":4866},[1736,23271,23272],{"class":1912}," { createTheme } ",[1736,23274,5002],{"class":4866},[1736,23276,23277],{"class":1935}," '@teambit/base-react.theme.theme-provider'\n",[11,23279,23280,23281,23285],{},"Then we just need to create our theme and use our provider. This complete version of the ",[15,23282,23284],{"href":23257,"rel":23283},[19],"Theme Provider Context"," component I created also leverages the context to be able to add theme togglers for changing to dark and light mode or other themes. I won't dive into this in this post so below is just a basic example of how the theme can handle a theme override.",[299,23287,23289],{"className":4894,"code":23288,"language":4896,"meta":307,"style":307},"\u003CBaseTheme.ThemeProvider overrides={customTheme}>\n  {children}\n\u003C/BaseTheme.ThemeProvider>\n",[179,23290,23291,23306,23311],{"__ignoreMap":307},[1736,23292,23293,23295,23298,23301,23303],{"class":1738,"line":1739},[1736,23294,6657],{"class":1912},[1736,23296,23297],{"class":1918},"BaseTheme.ThemeProvider",[1736,23299,23300],{"class":2674}," overrides",[1736,23302,5062],{"class":4866},[1736,23304,23305],{"class":1912},"{customTheme}>\n",[1736,23307,23308],{"class":1738,"line":748},[1736,23309,23310],{"class":1912},"  {children}\n",[1736,23312,23313,23315,23317],{"class":1738,"line":756},[1736,23314,8105],{"class":1912},[1736,23316,23297],{"class":1918},[1736,23318,6663],{"class":1912},[23,23320,23322],{"id":23321},"the-pink-theme","The Pink Theme",[11,23324,23325,23326,891],{},"The Theme provider comes with an overrides property so that we can add in any theme and it will merge with our base theme and override any duplicated design tokens. This means we can create a pink theme where only the colors change. However if we wanted to also change the font families then we would just add a design tokens file for font families inside our ",[15,23327,23330],{"href":23328,"rel":23329},"https://bit.cloud/learn-bit-react/base-ui/themes/pink-theme",[19],"pink theme component",[11,23332,23333],{},[121,23334],{"alt":23335,"src":23336},"design tokens for a pink theme showing the colors and var names","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648117439/debbie.codes/blog/2022/pink-theme_2x_iyuccs.png",[23,23338,23340],{"id":23339},"build-components-or-build-your-design-system","Build Components or Build your Design System?",[11,23342,23343],{},"We have just built our theming and design tokens so now it's time to build some components. Some people start with a component library or enhance the design system and start building all the components that would make up this design system such as buttons, inputs, cards etc. if you have time to do this and a team dedicated to it then this is great but most people don't, so building the design system as they go along is also a valid option.",[23,23345,23347],{"id":23346},"naming-your-components","Naming your Components",[11,23349,23350],{},"We haven't started with the task at hand yet so lets do that. Again lets talk to our designers and make sure we are on the same page when it comes to naming.",[11,23352,23353],{},[121,23354],{"alt":23355,"src":23356},"wire frame of how cart page should look","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648117081/debbie.codes/blog/2022/cart-wire-frame_jwitru.png",[11,23358,23359],{},"When building out the wire frames it is a good idea to sit down with the designer and think about the components that are being designed. We start with a page component called cart and we even think about where this component lives, in which folder or in which scope or to which team it belongs to. The cart component contains the heading component, which is a base-ui component that will be used throughout the site.",[11,23361,23362],{},"We can use the wire frame to add naming to our components. That way when the designer goes to build the design they will use the names that are on this wire frame and the developers will also use these names. Components in Figma should also have the same folder structure and ownership as those the developer uses. This also means if new designers or developers are on-boarded it is much easier for them to find things especially developers who need to see both designs and available components.",[23,23364,6434],{"id":6433},[11,23366,23367],{},"With a design it can sometimes be hard to see the breakdown of components but with a wire frame it is much easier. We can then convert this wire frame into a list of components that we need to create.",[70,23369,23370,23373,23376,23379,23382,23385,23387,23390,23392],{},[73,23371,23372],{},"cart page",[73,23374,23375],{},"cart",[73,23377,23378],{},"heading",[73,23380,23381],{},"cart items",[73,23383,23384],{},"image",[73,23386,304],{},[73,23388,23389],{},"currency",[73,23391,14849],{},[73,23393,23394],{},"remove from cart",[11,23396,23397],{},"If we already had a component library created we could then add a tick next to the components that we already posses and work on creating the ones we don't. As we have no component library created we can now start thinking about the API for each component.",[23,23399,23401],{"id":23400},"component-apis","Component API's",[11,23403,23404],{},"We want our components to be used throughout our app and consumed by various developers. But we don't want to over architect our component either so we need to think about what we need for this component to work and how it should be best implemented.",[11,23406,23407],{},"We can also think about every use case there is and start building that into our component but sometime this isn't the best idea and we may end up with a component that does way too many things and it is extremely hard to take functionality away once a component has been adopted. It is much easier to later add some functionality if and when it is needed.",[11,23409,23410,23411,23414,23415,23417,23418,23420,23421,23424,23425,23427],{},"Our heading component should contain an ",[179,23412,23413],{},"element"," prop which allows the consumer to choose between h1, h2, h3 etc. An image component should have an ",[179,23416,6939],{}," prop, a ",[179,23419,6723],{}," prop and perhaps a ",[179,23422,23423],{},"loading"," prop for lazy loading options. A text component could contain an ",[179,23426,23413],{}," prop to render a p tag or a span tag.",[11,23429,23430,23431,23433],{},"A button component should be a basic button component that may accept props for adding styles so as to have primary or secondary buttons for example. It should also allow an ",[179,23432,8989],{}," prop so it can be used in different circumstances.",[11,23435,23436],{},[121,23437],{"alt":23438,"src":23439},"properties of a button component","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648121414/debbie.codes/blog/2022/button-props_2x_rwq5ea.png",[11,23441,23442],{},"A remove from cart component should have the functionality of removing a component from a cart and include the button component. This means consistency will be kept across all apps as the button component which contains the styling is used inside these wrapper components that add the functionality needed.",[299,23444,23446],{"className":4894,"code":23445,"language":4896,"meta":307,"style":307},"export type RemoveShoeFromCartProps = {\n  /**\n   * item in cart\n   */\n  cartItem: Shoe\n}\n\nexport function RemoveShoeFromCart({ cartItem }: RemoveShoeFromCartProps) {\n  const context = useContext(ShoeCartContext)\n  return (\n    \u003CButton\n      className={styles.removeFromCart}\n      secondary\n      onClick={() => context.removeProductFromCart(cartItem)}\n      aria-label=\"Remove from Cart\">\n      X\n    \u003C/Button>\n  )\n}\n",[179,23447,23448,23461,23465,23469,23473,23483,23487,23491,23512,23525,23531,23538,23547,23552,23570,23582,23587,23595,23599],{"__ignoreMap":307},[1736,23449,23450,23452,23454,23457,23459],{"class":1738,"line":1739},[1736,23451,6632],{"class":4866},[1736,23453,6635],{"class":4866},[1736,23455,23456],{"class":2674}," RemoveShoeFromCartProps",[1736,23458,4911],{"class":4866},[1736,23460,4914],{"class":1912},[1736,23462,23463],{"class":1738,"line":748},[1736,23464,6821],{"class":6820},[1736,23466,23467],{"class":1738,"line":756},[1736,23468,13730],{"class":6820},[1736,23470,23471],{"class":1738,"line":1755},[1736,23472,6831],{"class":6820},[1736,23474,23475,23478,23480],{"class":1738,"line":1761},[1736,23476,23477],{"class":5036},"  cartItem",[1736,23479,1087],{"class":4866},[1736,23481,23482],{"class":2674}," Shoe\n",[1736,23484,23485],{"class":1738,"line":1767},[1736,23486,1976],{"class":1912},[1736,23488,23489],{"class":1738,"line":1772},[1736,23490,1747],{"emptyLinePlaceholder":790},[1736,23492,23493,23495,23497,23500,23502,23504,23506,23508,23510],{"class":1738,"line":1778},[1736,23494,6632],{"class":4866},[1736,23496,6674],{"class":4866},[1736,23498,23499],{"class":2674}," RemoveShoeFromCart",[1736,23501,7233],{"class":1912},[1736,23503,14733],{"class":5036},[1736,23505,7239],{"class":1912},[1736,23507,1087],{"class":4866},[1736,23509,23456],{"class":2674},[1736,23511,7246],{"class":1912},[1736,23513,23514,23516,23518,23520,23522],{"class":1738,"line":1784},[1736,23515,7824],{"class":4866},[1736,23517,14682],{"class":1918},[1736,23519,4911],{"class":4866},[1736,23521,14687],{"class":2674},[1736,23523,23524],{"class":1912},"(ShoeCartContext)\n",[1736,23526,23527,23529],{"class":1738,"line":1790},[1736,23528,6685],{"class":4866},[1736,23530,6688],{"class":1912},[1736,23532,23533,23535],{"class":1738,"line":1796},[1736,23534,6693],{"class":1912},[1736,23536,23537],{"class":1918},"Button\n",[1736,23539,23540,23542,23544],{"class":1738,"line":2353},[1736,23541,11932],{"class":2674},[1736,23543,5062],{"class":4866},[1736,23545,23546],{"class":1912},"{styles.removeFromCart}\n",[1736,23548,23549],{"class":1738,"line":2358},[1736,23550,23551],{"class":2674},"      secondary\n",[1736,23553,23554,23557,23559,23561,23563,23565,23567],{"class":1738,"line":2364},[1736,23555,23556],{"class":2674},"      onClick",[1736,23558,5062],{"class":4866},[1736,23560,14820],{"class":1912},[1736,23562,7013],{"class":4866},[1736,23564,14825],{"class":1912},[1736,23566,14828],{"class":2674},[1736,23568,23569],{"class":1912},"(cartItem)}\n",[1736,23571,23572,23575,23577,23580],{"class":1738,"line":2370},[1736,23573,23574],{"class":2674},"      aria-label",[1736,23576,5062],{"class":4866},[1736,23578,23579],{"class":1935},"\"Remove from Cart\"",[1736,23581,6663],{"class":1912},[1736,23583,23584],{"class":1738,"line":2376},[1736,23585,23586],{"class":1912},"      X\n",[1736,23588,23589,23591,23593],{"class":1738,"line":2381},[1736,23590,6744],{"class":1912},[1736,23592,9089],{"class":1918},[1736,23594,6663],{"class":1912},[1736,23596,23597],{"class":1738,"line":2387},[1736,23598,6753],{"class":1912},[1736,23600,23601],{"class":1738,"line":2393},[1736,23602,1976],{"class":1912},[23,23604,23606],{"id":23605},"creating-our-components","Creating our components",[11,23608,23609],{},"Once we have defined everything we can then go ahead and start creating our components. I would suggest starting with the biggest component, the page component in this case. As we are building the page component we can simply add html elements and placeholders for each component. Basically like building the wire frame inside your page component.",[11,23611,23612],{},"In order to make sure my component stays true to the design I like to import the Figma file right into my component docs. That way I can see the design against my composition to see if it is correct. It also makes it easier for me and any other developer to easily open the Figma file for that component without having to search through Figma. And as it is a Figma embed any changes to the Figma file or updated live in my component docs making it also easier for designers and product managers to make sure changes have been implemented.",[11,23614,23615,23616,23621],{},"I have done this by creating a ",[15,23617,23620],{"href":23618,"rel":23619},"https://bit.cloud/learn-bit-react/base-ui/figma/figma-embed",[19],"Figma Embed component"," and adding it to the doc file of my component.",[11,23623,23624],{},[121,23625],{"alt":23626,"src":23627},"figma embed showing a button component from figma","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648130745/debbie.codes/blog/2022/figma-embed_2x_yn5u0p.png",[11,23629,23630],{},"We can then go ahead and start building the next biggest component that the cart page component needs. In our case it is the cart component that contains all the elements of the cart. You may ask why we should even create this as a component and instead just render it in the page component?",[11,23632,23633],{},"The reason is that the cart component, containing all the items of the cart, might also be used in other areas of your app for example when hovering over the cart icon or in a collapsible sidebar when browsing the site. Making it a component means it can easily be added to any other place on your site and if it is an independent deployable component it can be used on any other e-commerce site.",[11,23635,23636],{},"Continuing on with our components the next one is the heading component so we start building that working with the API we have defined to allow different heading elements to be passed in. We then import that component into our page component adding the element of h1. And we continue with this process until our cart page is complete.",[11,23638,23639],{},[121,23640],{"alt":23641,"src":23642},"image of a cart page with various items in the cart","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648116916/debbie.codes/blog/2022/cart-design_qrgd17.png",[23,23644,23646],{"id":23645},"working-as-a-team","Working as a Team",[11,23648,23649],{},"If there is more than one person working on building out this page then there is no issues at all as each component is built separately and then imported into the cart page. What is important is deciding on who is doing what so that no duplication happens and also making sure there is a central place for viewing components that have already been created and that these components are easy to find.",[23,23651,23653],{"id":23652},"document-and-test-your-components","Document and Test your Components",[11,23655,23656],{},"More importantly making sure your components are easy to use and understand with great documentation and examples is key if you want your components to be adopted by other developers. Creating compositions of your component with different use cases will help developers see how it can be used and writing tests for these components is extremely important so developers can really trust your components.",[11,23658,23659],{},[121,23660],{"alt":23661,"src":23662},"tests for a button component","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648122803/debbie.codes/blog/2022/button-tests_2x_j7a35h.png",[23,23664,3294],{"id":3293},[11,23666,23667],{},"Taking the time to think about what you are building will really save you a lot of time in the long run. Maybe you don't need a different theme now but you perhaps will in the future. If we build our components with scaling in mind it really will be easier to scale. Design Tokens help us do that.",[11,23669,23670],{},"Using CSS vars is a great way of making sure your components can be themed. But it's not just theming. Sometimes the design changes. Designers might re-write design token values and if they do it should be just a matter of updating those values in your design tokens files in your theme and your components will then receive these new values without having to do any modifications to the components themselves.",[11,23672,23673],{},"I hope you enjoyed the post. Good luck and have fun and reach out to me if you have any questions at all.",[23,23675,5706],{"id":5705},[70,23677,23678,23684,23690,23696,23702,23709],{},[73,23679,23680],{},[15,23681,23683],{"href":23033,"rel":23682},[19],"Base Theme Component",[73,23685,23686],{},[15,23687,23689],{"href":23328,"rel":23688},[19],"Pink Theme Component",[73,23691,23692],{},[15,23693,23695],{"href":23257,"rel":23694},[19],"Theme Context Provider",[73,23697,23698],{},[15,23699,23701],{"href":23162,"rel":23700},[19],"Design Tokens Viewer",[73,23703,23704],{},[15,23705,23708],{"href":23706,"rel":23707},"https://bit.cloud/learn-bit-react/shoe-store/apps/shoe-store",[19],"Shore Store App with Theming",[73,23710,23711],{},[15,23712,23715],{"href":23713,"rel":23714},"https://bit.cloud/learn-bit-react/theming/tutorial",[19],"Theming Tutorial",[2011,23717,23718],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":307,"searchDepth":748,"depth":748,"links":23720},[23721,23722,23723,23724,23725,23726,23727,23728,23729,23730,23731,23732,23733],{"id":23023,"depth":748,"text":23024},{"id":23237,"depth":748,"text":23238},{"id":23247,"depth":748,"text":23248},{"id":23321,"depth":748,"text":23322},{"id":23339,"depth":748,"text":23340},{"id":23346,"depth":748,"text":23347},{"id":6433,"depth":748,"text":6434},{"id":23400,"depth":748,"text":23401},{"id":23605,"depth":748,"text":23606},{"id":23645,"depth":748,"text":23646},{"id":23652,"depth":748,"text":23653},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2022-03-24","v1648117439/debbie.codes/blog/2022/pink-theme_2x_iyuccs.png",{"ogImage":23737,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648117439/debbie.codes/blog/2022/pink-theme_2x_iyuccs.png","/blog/design-tokens-and-theming",{"title":23006,"description":23011},"blog/design-tokens-and-theming",[5221],"lV8IvuRORHGtb5dUzrydyzedyWk_lUx86nuRJdMNsB4",{"id":23744,"title":23745,"body":23746,"canonical":788,"date":23892,"description":23893,"extension":786,"featured":787,"image":23894,"meta":23895,"navigation":790,"ogimage":788,"path":23896,"provider":5235,"published":787,"seo":23897,"stem":23898,"tags":23899,"url":788,"__hash__":23900},"blog/blog/discord-virtual-office.md","Discord as a Virtual Office",{"type":8,"value":23747,"toc":23879},[23748,23757,23760,23764,23770,23777,23781,23784,23788,23791,23794,23797,23803,23807,23810,23814,23817,23821,23824,23828,23831,23837,23841,23844,23848,23851,23857,23860,23864,23867,23869,23876],[11,23749,23750,23751,23756],{},"Recently I changed jobs and changing jobs always means new things such as learning how the company works, how to work as a team and how to collaborate on things. When I was told in my interview that ",[15,23752,23755],{"href":23753,"rel":23754},"https://bit.dev",[19],"Bit"," uses Discord for their virtual office I just presumed that it works the same as how we use Discord in all the servers I am a member of. But that wasn't the case at all. Let me tell you about it.",[11,23758,23759],{},"First let's understand the problem we are having when it comes to working across remote teams. Before in a normal office it was really easy to know who was at work, who was available, who could be brought into a meeting to discuss something. People were visible in the same office space. I live in Spain and Spanish companies don't seem to like the remote idea or didn't before they were forced into trying it. So I am very used to going to an office and being there and seeing others. And it works well. If you have a problem you walk over to the team and start asking a question and before you know it, it has turned into a discussion. So how can we replicate this same way of working?",[23,23761,23763],{"id":23762},"discord-to-the-rescue","Discord to the rescue",[11,23765,23766,23769],{},[15,23767,23755],{"href":23753,"rel":23768},[19]," chose to use discord as their virtual office and I have to say I am amazed at how well it works and how easy it is to set it up. How it works is we have voice channels set up with different names such as Open Space, Big Meeting Room, one on ones, and many more including a dining room.",[11,23771,23772,23773,23776],{},"My first thoughts on this were, that's great but it's just voice channels in Discord. We had that in Nuxt, Cloudinary and other servers I am a part of. And yes we use it for meetings and we shared screen and turned on video and it was great so how are we doing things different at ",[15,23774,23755],{"href":23753,"rel":23775},[19],"?",[23,23778,23780],{"id":23779},"what-problem-are-we-trying-to-solve","What Problem are we trying to Solve",[11,23782,23783],{},"Remember the problem we are trying to solve. How do we recreate the office look and feel? How do we know when people are available? How do we know who is working and when? Remote working has given us the freedom to work whenever we want and right now I certainly don't have a set time that I start work or finish. It varies every day depending on what I want/need to do, what sport I decide to do or if it's raining, cause it's not much fun doing sport in the rain. Then there is timezones, trying to figure out what time it is in America just to wonder if someone is probably working or not, which should be easy but for some reason it is not.",[23,23785,23787],{"id":23786},"so-what-is-the-solutions","So what is the solutions",[11,23789,23790],{},"It's actually quite simple. When we start work we join a voice channel otherwise known as a virtual room. Actually right now as I write this blog post I am in one of those rooms. It's 7am on a Saturday morning so I am the only one in the office but thats ok.",[11,23792,23793],{},"On a normal working day everyone should join a room. This makes it easy for everyone to see who is working. Now this is not so managers can see who is working and count how many hours people are doing. It's not like that at all. This is so the team can see who is around, who is available, incase needed for pair programming, discussions or any issues that might come up.",[11,23795,23796],{},"So if you can imagine 15 different voice channels set up and you start your day and jump into one. Now you might think, that's going to be very noisy, and it might be depending on the room you join. But you can jump to another room and if that one has too much going on, you can also just deafen the server so you don't hear anything cause maybe you are not at your desk cause you need to hang the washing out or make a coffee or whatever so that means someone knows you are working, you are around but if someone wants to talk to you you're not listening. This is when messages come in.",[11,23798,23799],{},[121,23800],{"alt":23801,"src":23802},"Discord Virtual Office","https://res.cloudinary.com/debsobrien/image/upload/q_auto,f_auto/v1617526504/debbie.codes/blog/discord-virtual-office_gdebzt.png",[23,23804,23806],{"id":23805},"slack-for-messages","Slack for Messages",[11,23808,23809],{},"We use slack for messages as slack just has better threads and it just works well for us for writing so if you want someones attention you just send them a slack message and they will get back to you when they are finished making their coffee or whatever.",[23,23811,23813],{"id":23812},"deep-focus","Deep Focus",[11,23815,23816],{},"Sometimes you just don't want to be disturbed and that's ok too. There is a particular room for that where you can't speak and therefore won't hear anything so it's a pretty quiet place and good for when you need to focus. I have to admit that I am yet to enter that room. But by going in there people know you are working and that they will be able to reach you at some point in the near future once you have time to respond to the slack message.",[23,23818,23820],{"id":23819},"dining-room","Dining Room",[11,23822,23823],{},"Same goes for the dining room. This is not a room we go to to have lunch and talk. At the end of the day virtual eating in front of each other doesn't work and isn't that great an experience. The lunch room is basically a way of telling your colleagues that you are at lunch so might be available on slack or might not but that you will be gone for a short while and will be back.",[23,23825,23827],{"id":23826},"daily","Daily",[11,23829,23830],{},"We have a daily meeting in one of the Big meeting rooms and admins have the power of actually virtually kidnapping you from whatever room you are in and dropping you into the meeting room so there are no excuses for missing it at all. This is a great feature and of course if you happen to be on an external call you will have the server deafened and therefore won't hear things which is probably what you want. On the daily you can turn on video if you want to or share a screen to showcase something. Having a daily with the team is really beneficial as you get to keep up with what is happening as well as be able to ask someone for help on a specific topic.",[11,23832,23833],{},[121,23834],{"alt":23835,"src":23836},"Virtual Office Daily","https://res.cloudinary.com/debsobrien/image/upload/q_auto,f_auto/v1617546733/debbie.codes/blog/discord-virtual-office-daily_2x_hwpicw.png",[23,23838,23840],{"id":23839},"recording-studio","Recording Studio",[11,23842,23843],{},"When it comes to the recording studio, the mic is not activated so you can't talk. This room is great for letting people know that you are recording something or live streaming therefore you are around but it's going to be impossible for someone to get hold of you until you finish.",[23,23845,23847],{"id":23846},"external-meeting-room","External Meeting Room",[11,23849,23850],{},"We also have an external meeting room for when we need to invite people who are not part of our team. This is great for when we don't want to use another platform or setup a zoom call just to get someone involved in a conversation for help and debugging. Recently we had a webpack issue we needed to solve so we invited one of the webpack maintainers who wasn't part of our team to join the call and help us debug our webpack loaders issues, when trying to migrate to webpack 5. The external meeting room allows us to easily invite people in, to collaborate with other people, debug code together and at the same time network and get to meet other people.",[11,23852,23853],{},[121,23854],{"alt":23855,"src":23856},"external meeting room","https://res.cloudinary.com/debsobrien/image/upload/q_auto,f_auto/v1617874852/debbie.codes/blog/external-room-discord_2x_yoi7kh.png",[11,23858,23859],{},"It is starting to become one of my favorite rooms and I look forward to seeing who else we will invite in there and what other pair programming sessions and networking we will do.",[23,23861,23863],{"id":23862},"day-to-day","Day to Day",[11,23865,23866],{},"So how it works from day to day is that we join a room and then when we have an issue such as a bug and we need help from someone we can see who is available and then jump in that room and simply ask a question. And that someone normally will respond. The great thing is that there might be other people in that room who might also respond. They can then learn from the issue you had or if they are not interested in listening they can just deafen the server.",[23,23868,3294],{"id":3293},[11,23870,23871,23872,23875],{},"Overall I have found the virtual office to be so beneficial. Before I pretty much worked alone. I felt lonely just the same as many dev advocates out there but in ",[15,23873,23755],{"href":23753,"rel":23874},[19]," it is different. For the first time I have not felt alone or lost. Instead I am surrounded by a great team of people who at the same time are available to me for anything I need and that is power. That is what makes a company move forward. And although I remain in the same room surrounded by the same 4 walls, for the first time I have felt that I am actually not alone and that just by jumping in a room in discord I ensure that I am not treated like a remote person but in fact someone who really is part of the team.",[11,23877,23878],{},"I for one am a peoples person so just being able to drop in on people and just say hi is mega beneficial especially in the world we live in when visiting others is just not part of the legal agenda. I have to say that during my first month at Bit I have never felt alone and have done so much team work it is just incredible. I really do mean it when I say that I really do love my job and a big part of it is of course because of the virtual office, therefore because of the company culture. For the first time working remote doesn't mean that I am alone. Working remote doesn't mean that I don't know what is going on day to day. Having a virtual office really means I can be a real part of the team and that is something I really just love.",{"title":307,"searchDepth":748,"depth":748,"links":23880},[23881,23882,23883,23884,23885,23886,23887,23888,23889,23890,23891],{"id":23762,"depth":748,"text":23763},{"id":23779,"depth":748,"text":23780},{"id":23786,"depth":748,"text":23787},{"id":23805,"depth":748,"text":23806},{"id":23812,"depth":748,"text":23813},{"id":23819,"depth":748,"text":23820},{"id":23826,"depth":748,"text":23827},{"id":23839,"depth":748,"text":23840},{"id":23846,"depth":748,"text":23847},{"id":23862,"depth":748,"text":23863},{"id":3293,"depth":748,"text":3294},"2021-04-03","We use Discord as our virtual office at Bit and I have to tell you all about it as it really is a great way to work and collaborate with others especially when working in different time zones.","photo-1614680376739-414d95ff43df?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop",{},"/blog/discord-virtual-office",{"title":23745,"description":23893},"blog/discord-virtual-office",[3464],"_vg9QTA0k9d-LZ-uHkwJi6kjXzAQLgb4wih9kRqDGl8",{"id":5,"title":6,"body":23902,"canonical":783,"date":784,"description":785,"extension":786,"featured":787,"image":788,"meta":24378,"navigation":790,"ogimage":788,"path":791,"provider":788,"published":790,"seo":24379,"stem":793,"tags":24380,"url":788,"__hash__":797},{"type":8,"value":23903,"toc":24344},[23904,23909,23911,23919,23921,23923,23925,23927,23931,23933,23935,23965,23967,23971,23973,23977,23979,23983,23985,23989,23993,23997,24001,24005,24007,24009,24013,24015,24017,24019,24021,24026,24028,24032,24034,24058,24060,24067,24069,24071,24073,24077,24082,24086,24092,24094,24096,24100,24102,24110,24119,24121,24129,24131,24133,24135,24140,24144,24146,24148,24150,24152,24154,24156,24158,24172,24174,24176,24178,24180,24182,24184,24186,24188,24190,24192,24196,24198,24200,24202,24204,24206,24208,24210,24212,24214,24218,24220,24222,24224,24226,24232,24234,24236,24238,24240,24242,24244,24248,24250,24258,24264,24268,24270,24272,24276,24280,24284,24288,24292,24294,24296,24310,24312,24314,24316,24318,24320,24322,24324,24326,24330,24332,24334,24342],[11,23905,13,23906,21],{},[15,23907,20],{"href":17,"rel":23908},[19],[23,23910,26],{"id":25},[11,23912,23913,34,23916,40],{},[15,23914,33],{"href":31,"rel":23915},[19],[15,23917,39],{"href":37,"rel":23918},[19],[11,23920,43],{},[23,23922,47],{"id":46},[11,23924,50],{},[11,23926,53],{},[11,23928,56,23929,61],{},[58,23930,60],{},[23,23932,65],{"id":64},[11,23934,68],{},[70,23936,23937,23941,23945,23949,23953,23957,23961],{},[73,23938,23939,78],{},[58,23940,77],{},[73,23942,23943,84],{},[58,23944,83],{},[73,23946,23947,90],{},[58,23948,89],{},[73,23950,23951,96],{},[58,23952,95],{},[73,23954,23955,102],{},[58,23956,101],{},[73,23958,23959,108],{},[58,23960,107],{},[73,23962,23963,114],{},[58,23964,113],{},[11,23966,117],{},[11,23968,23969],{},[121,23970],{"alt":123,"src":124},[23,23972,128],{"id":127},[11,23974,131,23975,136],{},[133,23976,135],{},[138,23978,141],{"id":140},[11,23980,23981],{},[121,23982],{"alt":146,"src":147},[11,23984,150],{},[11,23986,23987,156],{},[58,23988,155],{},[11,23990,23991,162],{},[58,23992,161],{},[11,23994,23995,168],{},[58,23996,167],{},[11,23998,23999,174],{},[58,24000,173],{},[11,24002,177,24003,182],{},[179,24004,181],{},[11,24006,185],{},[138,24008,189],{"id":188},[11,24010,24011],{},[121,24012],{"alt":194,"src":195},[11,24014,198],{},[200,24016,203],{"id":202},[11,24018,206],{},[11,24020,209],{},[11,24022,212,24023,218],{},[15,24024,217],{"href":215,"rel":24025},[19],[200,24027,222],{"id":221},[11,24029,24030],{},[121,24031],{"alt":227,"src":228},[11,24033,231],{},[70,24035,24036,24040,24046,24050,24054],{},[73,24037,24038,238],{},[58,24039,217],{},[73,24041,24042,247],{},[58,24043,243,24044],{},[179,24045,246],{},[73,24047,24048,253],{},[58,24049,252],{},[73,24051,24052,259],{},[58,24053,258],{},[73,24055,24056,265],{},[58,24057,264],{},[11,24059,268],{},[11,24061,271,24062,276,24065,280],{},[15,24063,275],{"href":215,"rel":24064},[19],[179,24066,279],{},[200,24068,284],{"id":283},[11,24070,287],{},[11,24072,290],{},[11,24074,293,24075,297],{},[58,24076,296],{},[299,24078,24080],{"className":24079,"code":303,"language":304},[302],[179,24081,303],{"__ignoreMap":307},[11,24083,310,24084,314],{},[179,24085,313],{},[11,24087,317,24088,321,24090,325],{},[179,24089,320],{},[179,24091,324],{},[11,24093,328],{},[138,24095,332],{"id":331},[11,24097,24098],{},[121,24099],{"alt":337,"src":338},[11,24101,341],{},[11,24103,24104,347,24106,351,24108,355],{},[58,24105,346],{},[179,24107,350],{},[179,24109,354],{},[11,24111,358,24112,362,24114,367,24117,370],{},[179,24113,361],{},[15,24115,366],{"href":37,"rel":24116},[19],[179,24118,181],{},[11,24120,373],{},[11,24122,24123,379,24125,383,24127,387],{},[58,24124,378],{},[179,24126,382],{},[179,24128,386],{},[11,24130,390],{},[138,24132,394],{"id":393},[11,24134,397],{},[11,24136,400,24137,406],{},[15,24138,405],{"href":403,"rel":24139},[19],[11,24141,409,24142,413],{},[179,24143,412],{},[11,24145,416],{},[11,24147,419],{},[23,24149,423],{"id":422},[11,24151,426],{},[11,24153,429],{},[11,24155,432],{},[11,24157,435],{},[11,24159,438,24160,444,24163,450,24166,456,24169,462],{},[15,24161,443],{"href":441,"rel":24162},[19],[15,24164,449],{"href":447,"rel":24165},[19],[15,24167,455],{"href":453,"rel":24168},[19],[15,24170,461],{"href":459,"rel":24171},[19],[23,24173,466],{"id":465},[138,24175,470],{"id":469},[11,24177,473],{},[138,24179,477],{"id":476},[11,24181,480],{},[11,24183,483],{},[11,24185,486],{},[138,24187,490],{"id":489},[11,24189,493],{},[11,24191,496],{},[11,24193,499,24194,503],{},[179,24195,502],{},[138,24197,507],{"id":506},[11,24199,510],{},[11,24201,513],{},[11,24203,516],{},[23,24205,520],{"id":519},[138,24207,524],{"id":523},[11,24209,527],{},[138,24211,531],{"id":530},[11,24213,534],{},[11,24215,537,24216,541],{},[179,24217,540],{},[138,24219,545],{"id":544},[11,24221,548],{},[23,24223,552],{"id":551},[138,24225,556],{"id":555},[11,24227,559,24228,563,24230,567],{},[179,24229,562],{},[179,24231,566],{},[11,24233,570],{},[138,24235,574],{"id":573},[11,24237,577],{},[138,24239,581],{"id":580},[11,24241,584],{},[23,24243,588],{"id":587},[11,24245,24246],{},[121,24247],{"alt":593,"src":594},[23,24249,598],{"id":597},[11,24251,24252,604,24254,608,24256,611],{},[58,24253,603],{},[179,24255,607],{},[179,24257,562],{},[11,24259,24260,617,24262,620],{},[58,24261,616],{},[179,24263,320],{},[11,24265,24266,626],{},[58,24267,625],{},[23,24269,630],{"id":629},[11,24271,633],{},[11,24273,24274,639],{},[58,24275,638],{},[11,24277,24278,645],{},[58,24279,644],{},[11,24281,24282,651],{},[58,24283,650],{},[11,24285,24286,657],{},[58,24287,656],{},[11,24289,24290,663],{},[58,24291,662],{},[23,24293,667],{"id":666},[11,24295,670],{},[11,24297,673,24298,608,24301,684,24304,688,24306,691,24308,694],{},[15,24299,678],{"href":676,"rel":24300},[19],[15,24302,683],{"href":681,"rel":24303},[19],[179,24305,687],{},[179,24307,678],{},[179,24309,683],{},[11,24311,697],{},[23,24313,701],{"id":700},[11,24315,704],{},[11,24317,707],{},[11,24319,710],{},[23,24321,714],{"id":713},[11,24323,717],{},[11,24325,720],{},[11,24327,723,24328,726],{},[179,24329,678],{},[11,24331,729],{},[731,24333],{},[11,24335,735,24336,739,24339,743],{},[15,24337,443],{"href":441,"rel":24338},[19],[15,24340,33],{"href":31,"rel":24341},[19],[11,24343,746],{},{"title":307,"searchDepth":748,"depth":748,"links":24345},[24346,24347,24348,24349,24355,24356,24362,24367,24372,24373,24374,24375,24376,24377],{"id":25,"depth":748,"text":26},{"id":46,"depth":748,"text":47},{"id":64,"depth":748,"text":65},{"id":127,"depth":748,"text":128,"children":24350},[24351,24352,24353,24354],{"id":140,"depth":756,"text":141},{"id":188,"depth":756,"text":189},{"id":331,"depth":756,"text":332},{"id":393,"depth":756,"text":394},{"id":422,"depth":748,"text":423},{"id":465,"depth":748,"text":466,"children":24357},[24358,24359,24360,24361],{"id":469,"depth":756,"text":470},{"id":476,"depth":756,"text":477},{"id":489,"depth":756,"text":490},{"id":506,"depth":756,"text":507},{"id":519,"depth":748,"text":520,"children":24363},[24364,24365,24366],{"id":523,"depth":756,"text":524},{"id":530,"depth":756,"text":531},{"id":544,"depth":756,"text":545},{"id":551,"depth":748,"text":552,"children":24368},[24369,24370,24371],{"id":555,"depth":756,"text":556},{"id":573,"depth":756,"text":574},{"id":580,"depth":756,"text":581},{"id":587,"depth":748,"text":588},{"id":597,"depth":748,"text":598},{"id":629,"depth":748,"text":630},{"id":666,"depth":748,"text":667},{"id":700,"depth":748,"text":701},{"id":713,"depth":748,"text":714},{},{"title":6,"description":785},[795,796],{"id":24382,"title":24383,"body":24384,"canonical":788,"date":24504,"description":24505,"extension":786,"featured":787,"image":24506,"meta":24507,"navigation":790,"ogimage":788,"path":24508,"provider":5235,"published":787,"seo":24509,"stem":24510,"tags":24511,"url":788,"__hash__":24512},"blog/blog/extrovert-in-virtual-world.md","Being an Extrovert in a Virtual World",{"type":8,"value":24385,"toc":24502},[24386,24389,24394,24397,24400,24404,24407,24410,24413,24416,24420,24423,24426,24430,24433,24437,24440,24444,24447,24450,24454,24457,24460,24463,24466,24470,24473,24476,24479,24483,24486,24495,24498],[11,24387,24388],{},"I started off the year going on a cruise ship for 16 days with over 20 speakers from around the world. Can you imagine what that is like? For those that love speaking at conferences and cherish the speaker dinner well imagine 16 speaker dinners plus breakfasts and lunches. Amazing. Of course you could do what you wanted and be on your own but nobody chose to. Most of us didn't know each other from before, yet we all just clicked so well that it wasn't just speaker dinner but card games, karaoke, quiz nights and endless chats and fun. It was literally out of this world, especially as we entered into the vastness of Antarctica.",[24390,24391],"cld-image",{"public-id":24392,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24393},"debbie.codes/artctica_speakers_kvfixk","Antarctica speakers",[11,24395,24396],{},"Of course February is for skiing and although I couldn't take that much time off after being on a cruise for 16 days, I could for sure take a long weekend and head off to ski. And that I did. Work was getting complicated and I wasn't really happy in my job so to be honest every time I was out of the office was kinda better for me. Skiing is fun and a great stress reliever with only the mountain and again like Antarctica an almost silence and peaceful atmosphere. Great for thinking.",[11,24398,24399],{},"February was filled with more travel. First a meetup in Valencia followed by Vue Amsterdam, one of my favourite conferences where I get to meet my Vue family. I get so much energy from this conference and it was my goal this year to speak at this conference on that big massive stage. And it really is big. As always I was shaking like hell with nerves. I always get nervous about giving a talk in front of the Chopin brothers, the creators of Nuxt. Not only that but it was my first time giving a talk in front of Sara Drasner, someone I looked up to so much. I ended the show and went out with a bang by doing a dance on the stage after Sébastien Chopin said I could be part of the Nuxt team if I did a dance live on stage. I mean only a dance you need, is that all. And of course an Irish jig it was.",[24390,24401],{"public-id":24402,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24403},"debbie.codes/vue_amsterdam_2_vkc631","Vue Amsterdam conference",[11,24405,24406],{},"Little did I know that a week later I would get a phone call from Sébastien and Alex asking me to come and work for Nuxt as head of Learning and Developer Advocate. Not just be part of the Open Source team but actually be paid and work full time for Nuxt. Of course I was scared, what if I wasn't good enough, what if I wasn't ready, but I wanted it. I knew deep down it was the right move and I knew I had to break my promise to my old job where I was meant to stay till the end of the year but like I said, I wasn't happy in the job and I needed a new beginning and a new challenge. Working for Nuxt was like all my dreams coming true.",[11,24408,24409],{},"Wow 2020 was going to be the best year ever. I was already booked for 3 more trips to the US, to Google and Microsoft conferences as well as Switzerland, Belgium and Greece.",[11,24411,24412],{},"Then 2 weeks after Vue Amsterdam and I was in Texas for Vue USA. I hadn't handed in my notice yet and my boss was asking if I really had to go to yet another conference. I said yes I do. Not just did I need to get out of the office as I wasn't happy there but I needed to be surrounded by people who understood me. The energy I get from seeing all my speaker friends is something that I can not explain. Texas was so much fun as I had a day before the conference of hanging out with Vue friends followed by the conference itself. And my goal of being part of all Vue conferences had been achieved as I had spoken at Vue Toronto the year before and Vue London as MC.",[11,24414,24415],{},"It was now March. Some speakers had pulled out. There was disinfectant on the tables. We were told to use elbows instead of shaking hands. But in the speaker room felt pretty normal as we all sat there close to each other and I for sure was hugging everyone not even really understanding what was going on on the other side of the world. But emails were coming in. Microsoft cancelled the MVP Summit. Google cancelled Google I/O. Cloudinary would soon follow suit. My travel plans for the next few weeks and month were slowly disappearing. Yet everything seemed ok, I mean yes we had some precautions and then on the last night we pack into a bar and dance the night away sining karaoke all night, sharing a microphone with anyone and everyone. This is what I would say was my last night of normality. If I had of known back then I probably would have stayed for a few more drinks and not gone home to be responsible and get up for my flight.",[24390,24417],{"public-id":24418,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24419},"debbie.codes/vue_texas_bnh2yr","Vue Texas Karaoke",[11,24421,24422],{},"When I arrived back to Spain things started to get worse. We were told to work remote, that soon we would go into lockdown. To be prepared as it was coming. That day I told my boss I was leaving as we packed up our stuff but I was giving 3 weeks notice. I wanted to finish off a big project I was working on for a client. So obviously I only packed the computer and not the things in my drawer like my jumper and shoes for when I was cold or my packets of snacks for when I was hungry. This lockdown was only going to be for 2 weeks max and then I would head to the office and bring a cake and have a few goodbye beers. Little did I know. Little did we all know.",[11,24424,24425],{},"I said goodbye to my team and clients over zoom. It was strange. Almost like it wasn't real. But I was excited to start my new job in Nuxt which was going to be remote anyway so working from home was normal. There are always things you need to get used to when starting a new job. How things work, meetings here and there, new technology, roadmaps and plans. It is always a tiring few weeks.",[24390,24427],{"public-id":24428,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24429},"debbie.codes/new_job_m0ptqj","virtual work call",[11,24431,24432],{},"It was now April. Spain was having one of the strictest lockdowns in the world. We were not allowed outside our door, not for sport, walking, nothing. Only to the local shop and even going there I tried to not do as it wasn't really a pleasant experience. Walking to the shop felt like everyone in the world had been removed and you were the only one on the streets. Kids were not allowed out and there was no sound anywhere. It was as if all the kids had gone on holiday. I lived for balcony parties were at 8pm we would come out on the balcony and clap the health care workers and dance for 2 songs and we would see people on the other balconies and just think, ok, we are not alone. It was very, very hard. Of all the things I have had to do in my life, lockdown was for sure one of the hardest.",[24390,24434],{"public-id":24435,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24436},"debbie.codes/empty_motorway_dkvlys","empty motorway",[11,24438,24439],{},"Luckily I had a job I loved so I was able to work and I pretty much just worked. Easter holidays. Forget it. I just worked. It got me through the day as I was doing something I liked and I only turned on the news and twitter once I had finished work which was at balcony party time. Weekends I spent on my roof terrace where I did sport. Taekwondo patterns or running round in circles where I managed to run 10km one day. It kept me sane. Also I had great neighbours whom we were able to chat to as there is a large see through fence so easy for social distancing but still being social. My birthday consisted of having drinks with the neighbours and I put on my denim dress and cowboy boots from Texas and danced and danced and danced and I got a broom stick and we did karaoke while the neighbour sang to a spoon. We had fun in a crazy world. It was necessary. We need to sometimes switch off and just have fun.",[24390,24441],{"public-id":24442,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24443},"debbie.codes/debbie_running_savyzn","running on terrace",[11,24445,24446],{},"During lockdown I had to go to the city to sign papers so I could work at Nuxt. My accountant had sent me a document which gave me permission to be on the street incase I was stopped by Police. That morning I walked into the city. It was so strange to see the city so empty. And you really realise that everything is closed when you kinda need to go the toilet and there isn't even a McDonalds open, not a single bar or shop. Nothing. After signing the papers I decided to take a detour, the long way home, so I could walk past the sea for just a few minutes. Not a soul on the street, not a boat in the ocean, not a plane in the sky, only the odd police car driving past. It really was a very weird and unreal experience that I hope I will never have to relive.",[11,24448,24449],{},"Lockdown lasted longer than we ever imagined and the day we were finally allowed out to do sport was so nice. I took my bike and cycled down to the sea. I saw people. It was so nice to see people, to hear chatter and just feel like there was life again in the city. There was actually a lot of people. Imagine locking everyone up for 2 months and then finally letting them all out at the same time. Well that is what it was like. Only we had timetables. So I could go out from 6am till 10am or from 8pm till 11pm. The rest of the hours were reserved for children or people over 70. As I cycled away from the sea and the crowds of people all out doing sport for the first time in months I smelt things I had not smelt for so long. Horse shit. I know. It smelt so good to just smell nature. Weird I know but that's how it was.",[24390,24451],{"public-id":24452,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24453},"debbie.codes/debbie_cycling_axdln0","cycling",[11,24455,24456],{},"In June life pretty much went back to normal, in a non normal kind of way. We could go out when we wanted, bars and restaurants were open and we could go to the gym again. Masks everywhere but at least we could get out. But you don't really go out. Have dinner at the odd restaurant terrance that isn't busy but not much else. Except for the beach of course, one of the few places you can go where you don't have to wear a mask.",[11,24458,24459],{},"During lockdown there were a lot of conference cancellations. There were also a lot of remote conferences. It was like everyone just decided they needed to hold a remote conference or meetup and it was pretty full on for a while. We speakers, or at least me, felt like we needed to create and share content and connect with people. They were of course extremely hard times. Some conferences got postponed with most of them thinking September was going to be the month when everything would be back to normal. Unfortunately that was not the case.",[11,24461,24462],{},"My first online experience was the MVP summit. It was good but not the same. There was a virtual bar and that was cool as we could just hang out with people after the talks. At the time you could only see 4 people in the Microsoft Teams app so when more people joined you didn't actually see them. I remember sending a message to my friend Jeff on twitter saying come to the bar and just say Hi Debbie so I can see you. And he did and I got so excited and said Hey Jeff, it is so good to see you. And it was. Seeing a familiar face was so nice.",[11,24464,24465],{},"During the lockdown a few speaker friends set up different ways for people to connect. Not everyone was going through a strict lockdown but people wanted to reach out and help others. There were virtual coffee breaks, virtual bars, virtual concerts and one guy even said \"I will be working, here is a zoom link if anyone wants to just join and say hi\". I of course did. Just for a few minutes as I needed to have a general chat with someone and have some sort of normality in my virtual world.",[24390,24467],{"public-id":24468,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24469},"debbie.codes/online_conf_ttmjml","online conference",[11,24471,24472],{},"And that's exactly what it was. We were now left in a virtual world with lots of conferences coming up which should be an exciting time as conferences mean so much to us speakers. Not just do we learn new things but we connect with people, we network, we share ideas, we talk, we laugh, we have fun. And as speakers we get to hang out with like minded people and it helps us grow and be better speakers as we all support each other so much. As for the conference it self. We get a buzz from the stage, walking out there to give your talk to hundreds of people not knowing if they will like it, how they will react or if you will remember what you want to say. Before all my talks I get so nervous. If you put my talk after lunch then I can't eat at all. The nerves are so strong but that is adrenaline. That gives me the energy to get on the stage and once I am there and connect with the audience, I come alive. I give my talk and I don't even remember what I said. And when it ends and the audience clap it is like you have overcome another challenge in life but also as you leave the stage and talk to the people in the corridors who tell you they liked your talk and they learnt a lot and you see the twitter messages of people who connected with your talk and thanking you for doing it. And that is why I spend hours writing a talk. Hours researching content. Hours finding funny images and gifs and thinking of a theme to go along with the talk so that it is not only technical but also fun. I like when people laugh, I like seeing and hearing it.",[11,24474,24475],{},"But the virtual world has taken all that away. There is no buzz, no networking, no feedback, no laughing, actually you don't even see a person's face. There is no fun anymore. We have lost everything that makes conferences fun. Right now they are not fun. Virtual life is boring. Setting up a zoom call with only you on it so you can see someones face, your face, while you record a conference talk is just wrong. We have some of the best tech solutions in the world yet we somehow haven't been able to make virtual life fun. We haven't been able to make virtual networking possible. We need to do more. We need to fix this.",[11,24477,24478],{},"Don't get me wrong, it is not just conferences and meetups but everything in general. From work to family calls or perhaps it is the lack of seeing people even if virtual. Some people don't have great internet connections so many times peoples cameras are not turned on meaning you don't see them. You don't see peoples reactions, you don't see peoples smiles. Do people even have smiles? We wear masks everywhere so we don't see people smiling anymore. It is kinda sad.",[24390,24480],{"public-id":24481,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24482},"debbie.codes/debbies_desk_ndamqo","empty desk with just computer and screens setup",[11,24484,24485],{},"I am an extrovert. That means I get my energy when surrounded by people. People are my energy. When I don't have interaction with people I just don't work like I should. Having a pair programming session for example gives me so much energy you would not believe. Having a podcast interview excites me as I talk to a person with a camera turned on and we have real life interaction. Don't get me wrong. I love working remote. But I like being around people. I like talking, laughing and running ideas by people. I like working on things together. We have the tools. We have the tech but I still feel we don't do enough things together. I know as well that some people are not like me. They prefer to not be around people. I respect that. We are all different.",[11,24487,24488,24489,24494],{},"The other day I was listening to the ",[15,24490,24493],{"href":24491,"rel":24492},"https://www.netlify.com/blog/2020/09/08/how-do-you-blog/",[19],"podcast created by Netlify",". Thank you to whoever created this. It was so nice as I personally know some of the team and listening to them made me feel like I was at the table with them. It was general chat and it was nice to listen to and to hear familiar voices. It was nice that it wasn't about learning something but more about just hanging out and chatting. I need more of that.",[11,24496,24497],{},"We need more virtual coffee breaks, virtual bars, virtual fun. We are not leaving this virtual world any time soon. We are not travelling any time soon and we are probably not going to be doing in house conferences any time soon. So let's figure out a way to connect with others so we can get the energy we need to be the people we are meant to be. Let's try and change things. Let's try and create more fun. Together we can make things better. And hopefully one day soon we can go back to the way things were and just hangout with people and have fun.",[24390,24499],{"public-id":24500,"width":6371,"fetchformat":6371,"quality":6371,"loading":6375,"alt":24501},"debbie.codes/virtual_bar_hdqba2","virtual bar",{"title":307,"searchDepth":748,"depth":748,"links":24503},[],"2020-09-12","From being surrounded by amazing people to being locked up from the world. 2020 has been an interesting year and a very hard one for those who are extroverts and love being around people.","photo-1545113972-bbccc3547465?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/extrovert-in-virtual-world",{"title":24383,"description":24505},"blog/extrovert-in-virtual-world",[3464],"tgbBpGAdhcS5qCP-_sK4f6nEenyfKjFtlhxK-MXNrAs",{"id":24514,"title":24515,"body":24516,"canonical":788,"date":24656,"description":24657,"extension":786,"featured":787,"image":24658,"meta":24659,"navigation":790,"ogimage":788,"path":24661,"provider":3460,"published":790,"seo":24662,"stem":24663,"tags":24664,"url":788,"__hash__":24665},"blog/blog/finding-motivation-as-a-developer.md","Finding Motivation as a Developer",{"type":8,"value":24517,"toc":24648},[24518,24521,24524,24530,24534,24537,24540,24543,24547,24550,24553,24556,24559,24562,24565,24569,24572,24575,24578,24583,24586,24590,24593,24596,24602,24605,24608,24612,24615,24621,24624,24627,24630,24634,24637,24641,24644],[11,24519,24520],{},"Many people keep asking me where I get my motivation from, how do I find so much time and energy to do so much?",[11,24522,24523],{},"I certainly don't have all the answers but I do think it is something you teach yourself. Life experiences, books, talks etc help you figure out what it is you want and then once you have goals it's all about how to apply your time to achieve those goals. Sounds easy right!",[11,24525,24526],{},[121,24527],{"alt":24528,"src":24529},"collection of inspiring books","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651832018/debbie.codes/blog/2022/inspiring-books_2x_ey0xyw.png",[23,24531,24533],{"id":24532},"me-at-16-years-old","Me at 16 years old",[11,24535,24536],{},"To be honest I have always been a very active person. I have always been involved in many things. At 16 years of age I was in secondary school, I was on the school hockey team and the school football team where we trained at lunch times and after school, I was practicing Taekwondo twice a week, I was going to drama school, I was working in the cloak room of a nightclub and I was working as petrol pump attendant.",[11,24538,24539],{},"My weekends were having to choose between a hockey match, a football match, drama school, a Viking Re-enactment (yes I used to be a Viking and re-enact battles with a spear or sword and shield as well as put on living history shows), or a Taekwondo competition. I would sometimes work in the nightclub till 4am and then get a lift to Dublin to compete in the Taekwondo competition that day. You would often find me asleep in the corner on top of my gym back between my fights. Funnily enough I used to win all my fights and have numerous trophies. Looking back, I really don't know how I did it.",[11,24541,24542],{},"It was pretty crazy but my parents had taught me that if I wanted to go to drama school I needed to find a way to pay for it. Same for Taekwondo and the bus fare etc. At 16 I was pretty independent. I knew what I wanted and found a way to make it happen. It did mean that I had no time for boyfriends at that age and very little time for TV. Luckily social media wasn't a thing back then and mobile phones were just starting to come out.",[23,24544,24546],{"id":24545},"achieving-goals","Achieving Goals",[11,24548,24549],{},"So fast forward many years and I am now working in tech and working remotely so how do I stay so motivated? For me it's all about goals. Goals are important and they can change, which is also fine. They can be long difficult goals to achieve or short easy goals.",[11,24551,24552],{},"For example after I got my 3rd degree in Taekwondo my goal was to get my 4th degree. This was a 4 year goal. The first few years it just meant training, competing, umpiring and teaching as I would normally do and if I missed a class or two it was no big deal.",[11,24554,24555],{},"Leading up to the 4th degree grading meant putting in extra time and having a clear training plan. I had a full time job so I needed to find time to train that worked around my job. I would get up every morning and put my suit on and on my roof terrace I would practice my patterns as the sun was rising. Every day, just half an hour was enough. Then I would go to work. At lunch time I would study Korean terminology. After work I would train.",[11,24557,24558],{},"I had no club at the time and no one to train with, so I would record myself doing a flying kick against a bag hanging in the gym and then watch it back to check the angle of the kick. To pass the exam I had to break some wood with this flying kick so the angle and exact spot had to be correct.",[11,24560,24561],{},"One of the patterns had a movement in it that was very hard for me to do so I made sure to practice this every day. Just a few minutes so the muscle would get used to it. Then once a month I would fly to London for training. Yes, I literally got on a 2 hour flight just so I could get some intensive training in which was amazing. This is where I took down all notes and then went away and worked on all the things I needed to fix. And a month later I would fly back and do it all over again.",[11,24563,24564],{},"This was dedication. I had a goal and I wanted to achieve that goal. And the fact I had no club, no instructor nearby was not going to stop me.",[23,24566,24568],{"id":24567},"giving-up-is-easy","Giving up is Easy",[11,24570,24571],{},"Sometimes we make excuses. We look at others who have succeeded and we compare ourselves to them and think how much we would like to do what they do but they have it easy and we don't. Of course those that lived in London had it much easier than me. They were training in a club with others various times a week. I was training on my roof terrace and beating up a boxing bag. I could have just given up and said it's not possible, it's too hard. But giving up is easy. Many people take the easy route and just give up which is why so many people don't make it to black belt.",[11,24573,24574],{},"The same goes for coding. When I became a mentor for OpenClassrooms (after first studying there and having had a mentor) it was incredible to see the amount of people who just gave up. They were paying to have a mentoring session and sometimes they just didn't show up or they would turn up and say, sorry I didn't have time to do anything today. One student even wanted to pay me money for every time he didn't do anything so he would be held accountable. This is just crazy. Believe me I know what it feels like to just want to give up, especially when it's hard.",[11,24576,24577],{},"The video below was filmed by OpenClassrooms in 2017 while I was studying JavaScript. I had just got rejected from a job interview but choose to keep going. The video was filmed with no script, no rehearsals, just me saying how I felt and I have watched it many times listening to my own words to remind myself never to give up.",[24579,24580,6376],"lite-youtube",{"videoid":24581,"playlabel":24582},"R8ZsDhf5Tis","CODING: How To Overcome The Biggest Obstacle When Learning Web Development",[11,24584,24585],{},"So how do you get up in the morning and decide to make time to achieve your goals? Sometimes it's just about making small habits. For me getting up an hour earlier and training on my roof terrace (or in the living room when it was raining) was a new habit I created. It wasn't that difficult as it didn't mean having to travel somewhere. Yes the floor wasn't ideal and neither was the space but you can make anything work if you really try hard enough.",[23,24587,24589],{"id":24588},"ok-so-now-what-motivates-me","Ok so now what motivates me?",[11,24591,24592],{},"To be honest Life motivates me right now. I enjoy living. Believe me there was a time when I didn't. I have been in deep dark holes not wanting to see the next day. I have spent days sitting on a sofa watching tv and not wanting to leave the house. Sometimes we all go through things. How we come out of it is what matters.",[11,24594,24595],{},"As you get older in life you also realise that your days are numbered. You are not going to be around forever. As much as my goal is to live till at least 100, it might not happen. We have no idea how long we have left so my goal is to live each day like there is no tomorrow. To enjoy life to the full and to not let the little things worry you. Although sometimes they do, believe me controlling that is sometimes hard. But I have a big sign on my wall that says \"Do more of what makes you happy\". And that is what I strive to do.",[11,24597,24598],{},[121,24599],{"alt":24600,"src":24601},"do more of what makes you happy","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651831378/debbie.codes/blog/2022/more-what-makes-happy_2x_h1ixha.png",[11,24603,24604],{},"I am very very lucky to have such an amazing career, something that I had to work very hard to achieve. But it means that I get paid to do something I love doing and is something many people don't have. People at the gym moan on a Monday morning about how it's Monday and they can't wait for the weekend.",[11,24606,24607],{},"I love Mondays. It's the start to another amazing week. Don't get me wrong. I know what it feels like to feel like that. To not like your job and just wish for the week to be over. But in tech I feel that most of us don't feel this way and that is pretty cool. Or perhaps it's just me.",[23,24609,24611],{"id":24610},"choosing-to-love-life-is-important","Choosing to love life is important",[11,24613,24614],{},"Choosing to do more of what makes you happy is important. Choosing to see the positive side of things is important. We all get to make these choices. There will always be people who have it better and easier than you and there will always be people who have it worse and harder than you.",[11,24616,24617],{},[121,24618],{"alt":24619,"src":24620},"change your thoughts and you and you can change your world","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651831376/debbie.codes/blog/2022/change-your-thoughts_2x_miwv05.png",[11,24622,24623],{},"Life itself is one big challenge. But it can be an amazing one if you let it. Figure out what your goals are, small goals, big goals and start working towards them. They could be learning a new language, studying something, giving a talk, writing a blog post, getting an new job etc. Then once you know what your goals are create small habits so that you can achieve those goals. And remember that we are not going to be here forever so stop putting things off and start working towards your goals today.",[11,24625,24626],{},"Start living and doing more of what makes you happy. For me it's sport, programming, learning, traveling and helping others. For my husband it's watching TV. We are all different. Don't compare yourself to others. Find what it is that makes you happy and start doing it.",[11,24628,24629],{},"I have talked a lot about Taekwondo in this post and in previous posts and you may be wondering if I am still training. The answer is no, which might come as a surprise. Right now Taekwondo isn't making me happy. Training alone and online is just not what I want to do and so I am doing more of what makes me happy which is more social sports such as Padel and cycling. I may return to it one day as it has been a major part of my life for over 25 years but for now I'm ok with letting go. This is really important, to let go of the things that are not making you happy.",[23,24631,24633],{"id":24632},"life-on-your-terms","Life on Your Terms",[11,24635,24636],{},"I leave you with this amazing video from Billy Yang about living life on your terms which I highly encourage you to watch.",[24579,24638,6376],{"videoid":24639,"playlabel":24640},"_EFk7nWoKw0","living life on your terms.",[11,24642,24643],{},"And I also recommend watching his recent one, The Good Life, filmed after the pandemic. I personally find them both very motivating.",[24579,24645,6376],{"videoid":24646,"playlabel":24647},"KyxXpweFck4","The Good Life",{"title":307,"searchDepth":748,"depth":748,"links":24649},[24650,24651,24652,24653,24654,24655],{"id":24532,"depth":748,"text":24533},{"id":24545,"depth":748,"text":24546},{"id":24567,"depth":748,"text":24568},{"id":24588,"depth":748,"text":24589},{"id":24610,"depth":748,"text":24611},{"id":24632,"depth":748,"text":24633},"2022-05-06","Many people keep asking me where I get my motivation from, how do I find so much time and energy to do so much? I certainly don't have all the answers but I do think it is something you teach yourself.","v1651832018/debbie.codes/blog/2022/inspiring-books_2x_ey0xyw.png",{"ogImage":24660,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651832018/debbie.codes/blog/2022/inspiring-books_2x_ey0xyw.png","/blog/finding-motivation-as-a-developer",{"title":24515,"description":24657},"blog/finding-motivation-as-a-developer",[3464,5495],"kabT-2PVTv_pEx-KkMJyBZqcAx5-7sorIs4ylKplbB4",{"id":24667,"title":24668,"body":24669,"canonical":788,"date":24673,"description":24674,"extension":786,"featured":787,"image":24675,"meta":24676,"navigation":790,"ogimage":788,"path":24677,"provider":3460,"published":787,"seo":24678,"stem":24679,"tags":24680,"url":24681,"__hash__":24682},"blog/blog/finding-your-way-around-the-vue-ui.md","Finding your way around the Vue UI",{"type":8,"value":24670,"toc":24671},[],{"title":307,"searchDepth":748,"depth":748,"links":24672},[],"2019-06-25","Vue CLI is the fastest way to get your Vue project off the ground and it also comes with a really cool graphical user interface that allows you to easily modify your project’s configuration, run linters, search for and install plugins, analyse your bundle with webpack and more.","v1630862638/debbie.codes/featured-posts/finding-way-round-vue-ui_gmgzf1",{"platform":6264},"/blog/finding-your-way-around-the-vue-ui",{"title":24668,"description":24674},"blog/finding-your-way-around-the-vue-ui",[5240],"https://ultimatecourses.com/blog/finding-your-way-around-the-vue-ui","0HGqqdAtBLOQ6aJy78tJbL5OLZRZew_o2CL2Svg4BqM",{"id":3324,"title":3325,"body":24684,"canonical":788,"date":3454,"description":3330,"extension":786,"featured":787,"image":3455,"meta":24769,"navigation":790,"ogimage":788,"path":3459,"provider":3460,"published":790,"seo":24770,"stem":3462,"tags":24771,"url":788,"__hash__":3465},{"type":8,"value":24685,"toc":24756},[24686,24688,24690,24692,24694,24696,24698,24700,24702,24704,24706,24708,24710,24712,24714,24716,24718,24720,24722,24724,24726,24728,24730,24732,24734,24736,24738,24740,24742,24744,24746,24748,24750,24752,24754],[11,24687,3330],{},[23,24689,3334],{"id":3333},[11,24691,3337],{},[23,24693,3341],{"id":3340},[11,24695,3344],{},[11,24697,3347],{},[138,24699,3351],{"id":3350},[11,24701,3354],{},[138,24703,3358],{"id":3357},[11,24705,3361],{},[11,24707,3364],{},[23,24709,3368],{"id":3367},[11,24711,3371],{},[11,24713,3374],{},[11,24715,3377],{},[11,24717,3380],{},[11,24719,3383],{},[11,24721,3386],{},[11,24723,3389],{},[11,24725,3392],{},[23,24727,3396],{"id":3395},[11,24729,3399],{},[11,24731,3402],{},[11,24733,3405],{},[23,24735,3409],{"id":3408},[11,24737,3412],{},[11,24739,3415],{},[23,24741,3419],{"id":3418},[11,24743,3422],{},[11,24745,3425],{},[23,24747,3429],{"id":3428},[11,24749,3432],{},[23,24751,3294],{"id":3293},[11,24753,3437],{},[11,24755,3440],{},{"title":307,"searchDepth":748,"depth":748,"links":24757},[24758,24759,24763,24764,24765,24766,24767,24768],{"id":3333,"depth":748,"text":3334},{"id":3340,"depth":748,"text":3341,"children":24760},[24761,24762],{"id":3350,"depth":756,"text":3351},{"id":3357,"depth":756,"text":3358},{"id":3367,"depth":748,"text":3368},{"id":3395,"depth":748,"text":3396},{"id":3408,"depth":748,"text":3409},{"id":3418,"depth":748,"text":3419},{"id":3428,"depth":748,"text":3429},{"id":3293,"depth":748,"text":3294},{"ogImage":3457,"loading":3458},{"title":3325,"description":3330},[3464],{"id":24773,"title":24774,"body":24775,"canonical":788,"date":24856,"description":24794,"extension":786,"featured":787,"image":24857,"meta":24858,"navigation":790,"ogimage":788,"path":24860,"provider":3460,"published":790,"seo":24861,"stem":24862,"tags":24863,"url":788,"__hash__":24864},"blog/blog/fixing-diversity-in-tech.md","Fixing Diversity in Tech",{"type":8,"value":24776,"toc":24851},[24777,24780,24783,24789,24792,24795,24798,24802,24805,24808,24811,24814,24820,24824,24827,24830,24833,24837,24840,24843,24848],[11,24778,24779],{},"As I fly home after an amazing conference at BeJs in Belgium, I look back on the the conversations had with the diverse group of speakers.",[11,24781,24782],{},"At this conference we got to give our talk in front of people and have a panel discussion involving the audience which was so much fun and then network after the talks. And as speakers we got to not just have dinner together but also hang out for a full day on a tour of Belgian cities with beer and chocolate. We got to know each other, spend a day together doing non tech things and we got to know the person behind the code.",[11,24784,24785],{},[121,24786],{"alt":24787,"src":24788},"group of speakers in front of beautiful buildings in the city of Ghent","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652638324/debbie.codes/blog/2022/speakers-ghent_r9bdnv",[11,24790,24791],{},"The speakers tour was very diverse with people from India, Tunisia, Morocco, Israel, Ireland, England, Greece, Lithuania, Bulgaria, Germany and Italy. People of different nationalities, different religions, different skin color, men, women, straight and gay. Nobody felt like the odd one out because we were all different, all unique, all special in our own way.",[11,24793,24794],{},"When you step back from the nationality, the color, the religion the sex etc you just get a bunch of people who become a great bunch of friends. This is tech. This is what tech should be like always, for everyone.",[11,24796,24797],{},"When you meet the people behind the code. When you get to know them and hear their stories. Some who struggle to get visas because of where they were born. People not accepted because of how they look, how they choose to dress, or simply because of their sex or color of skin. How can we make someone feel like they don't belong or the they are not good enough because of these things? It makes no sense at all.",[23,24799,24801],{"id":24800},"it-shouldnt-be-like-this","It shouldn't be like this",[11,24803,24804],{},"It shouldn't be like this. Not anymore. Yet it is. And we need to fix this. When I talk about trying to get more women in tech I am not trying to have women take over the tech industry and leave men with no place. That is no where near what it's about. I have no intention on forcing tech on anyone who doesn't want to be in this field. But those who do. I want to make sure they know that they do belong, they do deserve to be here and they do have a place in tech because tech is for everyone.",[11,24806,24807],{},"I am aware that it's not always easy and I have also been through tough times. I wanted to give up and walk away but had great mentors who ensured I didn't and thanks to them I am still here. When I write in my journal what my dream is, I always write that I want to be valued and respected. I write this yet this is surely something one should get by default. This shouldn't be something I have to wish for. Yet sometimes it is.",[11,24809,24810],{},"Unfortunately the world is not perfect. We as humans can be selfish, jealous and mean. We have to try harder to be better people. We have to try harder to just create a better world in tech.",[11,24812,24813],{},"I spent a lot of time with very talented developers, some who were almost half my age. They are our future and they are the ones who can ensure that this world is inclusive and diverse. As awareness is created and we begin to hear other peoples stories, we start to see that we still have a long way to go. But the young people of today want to help fix it and I think they really can and will achieve this.",[11,24815,24816],{},[121,24817],{"alt":24818,"src":24819},"group of speakers hanging out together and having fun","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652638321/debbie.codes/blog/2022/speakers-brussels_dneluw",[23,24821,24823],{"id":24822},"fixing-the-minority-gap","Fixing the minority gap",[11,24825,24826],{},"As for minority groups. The only way we can fix this is by being there. By not walking away. By not giving up. No matter how hard it is. And for some it will be hard, for some they will have to deal with difficult managers, difficult colleagues, be treated badly for no reason. We can't stop this just like that.",[11,24828,24829],{},"But we can slowly do it so the next generation grows up in a world where going to a conference won't mean they are one of few women, or people of color etc. It will mean they just feel like they belong, like they fit in.",[11,24831,24832],{},"This is obviously sometimes hard to do especially in certain countries. But when it comes to speakers, we can certainly make sure diversity is covered. And when someone in the audience looks on stage and sees another women, or person of color, or gay person or asian person etc they will feel like they do belong and they too can get on that stage and be the next speaker and help influence others.",[23,24834,24836],{"id":24835},"we-are-all-just-family","We are all just family",[11,24838,24839],{},"I am Irish and have Spanish family, Greek family and Bahraini family. We are all very different. We have different religion and customs, we speak different languages and hold different passports and look different. But to me, I don't see any of that. They are just my family. My cousins. My Irish cousins, my Greek cousins and my Bahraini cousins are just all my cousins.",[11,24841,24842],{},"And thats how you should treat people in tech as if they are all just your cousins from other walks of life.",[1713,24844,24845],{},[11,24846,24847],{},"Let's make a better world. Or at least let's try.",[11,24849,24850],{},"My DMs are always open if you ever feel like you don't belong or feel you are being treated badly by others. Reach out. Let's get through this together. Let's fix the diversity in tech.",{"title":307,"searchDepth":748,"depth":748,"links":24852},[24853,24854,24855],{"id":24800,"depth":748,"text":24801},{"id":24822,"depth":748,"text":24823},{"id":24835,"depth":748,"text":24836},"2022-05-15","v1652876872/debbie.codes/blog/2022/bejs-team_om4aqb.jpg",{"ogImage":24859,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652876872/debbie.codes/blog/2022/bejs-team_om4aqb.jpg","/blog/fixing-diversity-in-tech",{"title":24774,"description":24794},"blog/fixing-diversity-in-tech",[3464],"Pw6EJ67CbzyIu7JJvCYQuhHcmDE-wdoYW_2glNly32c",{"id":24866,"title":24867,"body":24868,"canonical":25070,"date":25071,"description":24872,"extension":786,"featured":787,"image":788,"meta":25072,"navigation":790,"ogimage":788,"path":25073,"provider":788,"published":790,"seo":25074,"stem":25075,"tags":25076,"url":788,"__hash__":25077},"blog/blog/fixing-failing-tests-automatically-with-playwrights-new-healer-agent.md","Fixing Failing Tests Automatically with Playwright's New Healer Agent",{"type":8,"value":24869,"toc":25062},[24870,24873,24877,24880,24885,24888,24895,24899,24902,24905,24937,24940,24944,24947,24957,24966,24969,24973,24976,24988,24991,24995,24998,25001,25016,25019,25040,25043,25047,25050,25053,25056,25059],[11,24871,24872],{},"If you've ever returned to your test suite to find a sea of red, you're going to love this one. The Playwright Healer Agent is here — and it's changing how we think about test maintenance.",[23,24874,24876],{"id":24875},"what-is-the-healer-agent","What Is the Healer Agent?",[11,24878,24879],{},"The Healer Agent is part of Playwright's new generation of AI-powered agents. Its goal is simple:",[1713,24881,24882],{},[11,24883,24884],{},"Automatically diagnose, repair, and re-run your failing tests.",[11,24886,24887],{},"Instead of spending time manually debugging selector changes or broken assertions, the Healer Agent can step in, analyze what's wrong, and apply fixes directly to your test code.",[11,24889,24890,24891,24894],{},"And when it can't fix something because the real problem is in your app or backend, it's smart enough to mark that test with ",[179,24892,24893],{},"test.fixme",", saving you time and frustration.",[23,24896,24898],{"id":24897},"watching-the-healer-in-action","Watching the Healer in Action",[11,24900,24901],{},"When I ran the agent, it immediately found nine failing tests. Then I walked away, literally went to grab a sandwich. By the time I came back, the agent had already finished its work.",[11,24903,24904],{},"Here's what it did under the hood:",[2260,24906,24907,24913,24925,24931],{},[73,24908,24909,24912],{},[58,24910,24911],{},"Debugged each failing test"," — stepping through line by line to understand the failure.",[73,24914,24915,24918,24919,24922,24923,891],{},[58,24916,24917],{},"Identified UI mismatches"," — for example, a test looking for a ",[179,24920,24921],{},"textbox"," when the app now uses a ",[179,24924,13027],{},[73,24926,24927,24930],{},[58,24928,24929],{},"Applied fixes"," — updated the locators and assertions to match the actual UI.",[73,24932,24933,24936],{},[58,24934,24935],{},"Re-ran the tests"," — verified that the fixes worked.",[11,24938,24939],{},"One particularly cool example: It saw that a test expected a heading with a certain regex pattern. But after checking a page snapshot, it found there were two separate heading elements. So it automatically adjusted the regex to make the test more flexible, then re-ran the test to confirm the fix.",[23,24941,24943],{"id":24942},"smarter-than-just-making-tests-pass","Smarter Than Just \"Making Tests Pass\"",[11,24945,24946],{},"One of my favorite moments was when the agent encountered a failing test that wasn't fixable through code alone.",[11,24948,24949,24950,24953,24954,24956],{},"The test was failing because the backend API wasn't responding, it saw a ",[179,24951,24952],{},"Failed to fetch"," error in the console logs. Instead of forcing a bad fix or skipping it silently, the agent marked the test as ",[179,24955,24893],{},", with a clear message explaining why:",[1713,24958,24959],{},[11,24960,24961,24962,24965],{},"\"Save operation is failing with ",[179,24963,24964],{},"fail to fetch"," error — likely a backend issue.\"",[11,24967,24968],{},"That's exactly how a responsible AI should behave — not hiding the problem, but documenting it.",[23,24970,24972],{"id":24971},"reviewing-the-results","Reviewing the Results",[11,24974,24975],{},"At the end of the run, the agent produced a summary:",[11,24977,24978,24979,24982,24983,952,24985,24987],{},"• ✅ 104 tests passing",[24980,24981],"br",{},"\n• ⚠️ 3 tests intentionally skipped (",[179,24984,24893],{},[24980,24986],{},"\n• 🧠 Clear explanations for every change",[11,24989,24990],{},"Every fix was committed right in the codebase, complete with comments and annotations. Reviewing those changes is as easy as opening your editor and scanning the updated tests.",[23,24992,24994],{"id":24993},"the-future-of-test-maintenance","The Future of Test Maintenance",[11,24996,24997],{},"This is a huge leap forward for automated testing. The Playwright Healer Agent doesn't just \"fix\" your tests — it understands them.",[11,24999,25000],{},"It's capable of:",[11,25002,25003,25004,25006,25007,25009,25010,25012,25013,25015],{},"• Reading and interpreting test logs",[24980,25005],{},"\n• Inspecting DOM snapshots",[24980,25008],{},"\n• Updating selectors, regex patterns, and assertions",[24980,25011],{},"\n• Recognizing backend or configuration issues",[24980,25014],{},"\n• Writing and verifying code fixes automatically",[11,25017,25018],{},"You can literally tell it:",[299,25020,25022],{"className":2665,"code":25021,"language":2667,"meta":307,"style":307},"run and fix my tests\n",[179,25023,25024],{"__ignoreMap":307},[1736,25025,25026,25028,25031,25034,25037],{"class":1738,"line":1739},[1736,25027,16218],{"class":2674},[1736,25029,25030],{"class":1935}," and",[1736,25032,25033],{"class":1935}," fix",[1736,25035,25036],{"class":1935}," my",[1736,25038,25039],{"class":1935}," tests\n",[11,25041,25042],{},"…and then walk away. When you return, your test suite will be passing (or clearly annotated), ready for your review.",[23,25044,25046],{"id":25045},"final-thoughts","Final Thoughts",[11,25048,25049],{},"The Healer Agent isn't just a convenience, it's a glimpse of where developer tooling is headed.",[11,25051,25052],{},"We're moving toward a world where AI doesn't just write code, but maintains and adapts it alongside us.",[11,25054,25055],{},"If you haven't tried it yet, give it a spin. Ask the Healer Agent to fix your failing tests, grab a coffee, and come back to review the magic.",[11,25057,25058],{},"👉 Happy healing — and happy testing!",[2011,25060,25061],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":25063},[25064,25065,25066,25067,25068,25069],{"id":24875,"depth":748,"text":24876},{"id":24897,"depth":748,"text":24898},{"id":24942,"depth":748,"text":24943},{"id":24971,"depth":748,"text":24972},{"id":24993,"depth":748,"text":24994},{"id":25045,"depth":748,"text":25046},"https://dev.to/debs_obrien/fixing-failing-tests-automatically-with-playwrights-new-healer-agent-13ck","2024-11-13",{},"/blog/fixing-failing-tests-automatically-with-playwrights-new-healer-agent",{"title":24867,"description":24872},"blog/fixing-failing-tests-automatically-with-playwrights-new-healer-agent",[1412,795,1411],"Oy9BKpoyC6aky3wxxAOx7PzJL2KHUcpWHcK2OjHrVeE",{"id":25079,"title":25080,"body":25081,"canonical":25537,"date":25551,"description":25552,"extension":786,"featured":790,"image":788,"meta":25553,"navigation":790,"ogimage":788,"path":25554,"provider":788,"published":790,"seo":25555,"stem":25556,"tags":25557,"url":788,"__hash__":25558},"blog/blog/generate-playwright-tests-without-code-access.md","Generate Playwright tests without code access using MCP and Copilot",{"type":8,"value":25082,"toc":25543},[25083,25086,25091,25094,25097,25101,25104,25115,25119,25130,25184,25187,25218,25221,25225,25232,25236,25254,25257,25261,25264,25480,25483,25494,25497,25500,25514,25522,25525,25528,25530,25540],[11,25084,25085],{},"Getting GitHub Copilot to write end-to-end tests usually requires giving it access to your source code. That’s because Copilot on its own can’t open a browser, explore a webpage, or understand how a UI behaves, so it lacks the context needed to generate accurate tests.",[11,25087,25088,25089,891],{},"But that changes with ",[58,25090,5584],{},[11,25092,25093],{},"An MCP acts as a bridge between Copilot and the real browser, allowing it to open websites, interact with UI elements, and take snapshots of the page. This unlocks a whole new capability: generating Playwright tests from natural language instructions, even when you don’t have access to the application’s codebase.",[11,25095,25096],{},"In this post, we’ll show how Copilot, with the help of the Playwright MCP, can generate, run, and validate E2E tests just by telling it what you want in plain English and with no access to the source code.",[23,25098,25100],{"id":25099},"why-not-just-use-copilot-alone","🧠 Why Not Just Use Copilot Alone?",[11,25102,25103],{},"GitHub Copilot is powerful—but it typically works best when it has access to your source code. If you're only interacting with a web app in the browser and need to generate tests, Copilot doesn’t have the context it needs.",[11,25105,25106,25107,25110,25111,25114],{},"That's where the ",[58,25108,25109],{},"Model Context Protocol (MCP)"," changes the game. It gives Copilot the ability to interact with the browser through a real-time interface, gather contextual data like ",[58,25112,25113],{},"ARIA snapshots",", and perform UI actions that can be converted into executable Playwright tests.",[23,25116,25118],{"id":25117},"️-setting-up-the-test-prompt","🛠️ Setting Up the Test Prompt",[11,25120,25121,25122,25125,25126,25129],{},"We start with a custom ",[58,25123,25124],{},"test generation prompt"," inside a ",[179,25127,25128],{},".github/prompts/generate.prompt.md"," file. It instructs Copilot to act as a test generator and use all available tools through the MCP agent.",[299,25131,25133],{"className":5843,"code":25132,"language":786,"meta":307,"style":307},"- You are a playwright test generator.\n- You are given a scenario and you need to generate a playwright test for it.\n- DO NOT generate test code based on the scenario alone. \n- DO run steps one by one using the tools provided by the Playwright MCP.\n- Only after all steps are completed, emit a Playwright TypeScript test that uses @playwright/test based on message history\n- Save generated test file in the tests directory\n- Execute the test file and iterate until the test passes\n",[179,25134,25135,25142,25149,25156,25163,25170,25177],{"__ignoreMap":307},[1736,25136,25137,25139],{"class":1738,"line":1739},[1736,25138,9419],{"class":5036},[1736,25140,25141],{"class":1912}," You are a playwright test generator.\n",[1736,25143,25144,25146],{"class":1738,"line":748},[1736,25145,9419],{"class":5036},[1736,25147,25148],{"class":1912}," You are given a scenario and you need to generate a playwright test for it.\n",[1736,25150,25151,25153],{"class":1738,"line":756},[1736,25152,9419],{"class":5036},[1736,25154,25155],{"class":1912}," DO NOT generate test code based on the scenario alone. \n",[1736,25157,25158,25160],{"class":1738,"line":1755},[1736,25159,9419],{"class":5036},[1736,25161,25162],{"class":1912}," DO run steps one by one using the tools provided by the Playwright MCP.\n",[1736,25164,25165,25167],{"class":1738,"line":1761},[1736,25166,9419],{"class":5036},[1736,25168,25169],{"class":1912}," Only after all steps are completed, emit a Playwright TypeScript test that uses @playwright/test based on message history\n",[1736,25171,25172,25174],{"class":1738,"line":1767},[1736,25173,9419],{"class":5036},[1736,25175,25176],{"class":1912}," Save generated test file in the tests directory\n",[1736,25178,25179,25181],{"class":1738,"line":1772},[1736,25180,9419],{"class":5036},[1736,25182,25183],{"class":1912}," Execute the test file and iterate until the test passes\n",[11,25185,25186],{},"Here’s a sample instruction we give to Copilot in plain English:",[299,25188,25190],{"className":5843,"code":25189,"language":786,"meta":307,"style":307},"Generate a Playwright test for the following scenario:\n1. Navigate to https://debs-obrien.github.io/playwright-movies-app\n2. search for 'Garfield'\n3. verify the movie is in the list\n",[179,25191,25192,25197,25204,25211],{"__ignoreMap":307},[1736,25193,25194],{"class":1738,"line":1739},[1736,25195,25196],{"class":1912},"Generate a Playwright test for the following scenario:\n",[1736,25198,25199,25201],{"class":1738,"line":748},[1736,25200,5860],{"class":5036},[1736,25202,25203],{"class":1912}," Navigate to https://debs-obrien.github.io/playwright-movies-app\n",[1736,25205,25206,25208],{"class":1738,"line":756},[1736,25207,5868],{"class":5036},[1736,25209,25210],{"class":1912}," search for 'Garfield'\n",[1736,25212,25213,25215],{"class":1738,"line":1755},[1736,25214,5876],{"class":5036},[1736,25216,25217],{"class":1912}," verify the movie is in the list\n",[11,25219,25220],{},"From here, Copilot and MCP take over.",[23,25222,25224],{"id":25223},"from-browser-interaction-to-test-code","🌐 From Browser Interaction to Test Code",[11,25226,25227,25228,25231],{},"Once the command is triggered in Copilot Chat (using ",[58,25229,25230],{},"Cloud 3.5 Sonnet"," in this example), the MCP spins up a browser session and starts executing the steps.",[138,25233,25235],{"id":25234},"key-highlights","Key highlights:",[70,25237,25238,25241,25248,25251],{},[73,25239,25240],{},"It navigates to the given URL using the Playwright MCP server.",[73,25242,25243,25244,25247],{},"It encounters a challenge locating the search field, so it uses a ",[58,25245,25246],{},"page snapshot"," to analyze the DOM.",[73,25249,25250],{},"It identifies the right input, types “Garfield,” and proceeds.",[73,25252,25253],{},"Finally, it auto-generates a Playwright test using those interactions.",[11,25255,25256],{},"The MCP provides Copilot with rich contextual insight about the page, like a vision system for your LLM.",[23,25258,25260],{"id":25259},"validating-the-test","✅ Validating the Test",[11,25262,25263],{},"Because our prompt included test validation, Copilot runs the test it just wrote, and it passes ✅",[299,25265,25269],{"className":25266,"code":25267,"language":25268,"meta":307,"style":307},"language-ts shiki shiki-themes github-light github-dark","import { test, expect } from '@playwright/test';\n\ntest('Movie search - Search for a movie by title', async ({ page }) => {\n  // Navigate to the movies app\n  await page.goto('https://debs-obrien.github.io/playwright-movies-app');\n\n  // Click on the search button to activate the search input\n  await page.getByRole('search').click();\n  \n  // Type 'Garfield' into the search input and press Enter\n  await page.getByRole('textbox', { name: 'Search Input' }).fill('Garfield');\n  await page.getByRole('textbox', { name: 'Search Input' }).press('Enter');\n\n  // Verify that 'The Garfield Movie' appears in the search results\n  await expect(page.getByRole('heading', { name: 'The Garfield Movie', level: 2 })).toBeVisible();\n});\n","ts",[179,25270,25271,25285,25289,25314,25319,25336,25340,25345,25364,25368,25373,25404,25432,25436,25441,25476],{"__ignoreMap":307},[1736,25272,25273,25275,25278,25280,25283],{"class":1738,"line":1739},[1736,25274,4996],{"class":4866},[1736,25276,25277],{"class":1912}," { test, expect } ",[1736,25279,5002],{"class":4866},[1736,25281,25282],{"class":1935}," '@playwright/test'",[1736,25284,7682],{"class":1912},[1736,25286,25287],{"class":1738,"line":748},[1736,25288,1747],{"emptyLinePlaceholder":790},[1736,25290,25291,25294,25296,25299,25301,25303,25305,25308,25310,25312],{"class":1738,"line":756},[1736,25292,25293],{"class":2674},"test",[1736,25295,7751],{"class":1912},[1736,25297,25298],{"class":1935},"'Movie search - Search for a movie by title'",[1736,25300,829],{"class":1912},[1736,25302,15790],{"class":4866},[1736,25304,17735],{"class":1912},[1736,25306,25307],{"class":5036},"page",[1736,25309,7778],{"class":1912},[1736,25311,7013],{"class":4866},[1736,25313,4914],{"class":1912},[1736,25315,25316],{"class":1738,"line":1755},[1736,25317,25318],{"class":6820},"  // Navigate to the movies app\n",[1736,25320,25321,25324,25326,25329,25331,25334],{"class":1738,"line":1761},[1736,25322,25323],{"class":4866},"  await",[1736,25325,22665],{"class":1912},[1736,25327,25328],{"class":2674},"goto",[1736,25330,7751],{"class":1912},[1736,25332,25333],{"class":1935},"'https://debs-obrien.github.io/playwright-movies-app'",[1736,25335,16106],{"class":1912},[1736,25337,25338],{"class":1738,"line":1767},[1736,25339,1747],{"emptyLinePlaceholder":790},[1736,25341,25342],{"class":1738,"line":1772},[1736,25343,25344],{"class":6820},"  // Click on the search button to activate the search input\n",[1736,25346,25347,25349,25351,25353,25355,25358,25360,25362],{"class":1738,"line":1778},[1736,25348,25323],{"class":4866},[1736,25350,22665],{"class":1912},[1736,25352,1032],{"class":2674},[1736,25354,7751],{"class":1912},[1736,25356,25357],{"class":1935},"'search'",[1736,25359,911],{"class":1912},[1736,25361,10804],{"class":2674},[1736,25363,15752],{"class":1912},[1736,25365,25366],{"class":1738,"line":1784},[1736,25367,16740],{"class":1912},[1736,25369,25370],{"class":1738,"line":1790},[1736,25371,25372],{"class":6820},"  // Type 'Garfield' into the search input and press Enter\n",[1736,25374,25375,25377,25379,25381,25383,25386,25388,25391,25394,25397,25399,25402],{"class":1738,"line":1796},[1736,25376,25323],{"class":4866},[1736,25378,22665],{"class":1912},[1736,25380,1032],{"class":2674},[1736,25382,7751],{"class":1912},[1736,25384,25385],{"class":1935},"'textbox'",[1736,25387,10685],{"class":1912},[1736,25389,25390],{"class":1935},"'Search Input'",[1736,25392,25393],{"class":1912}," }).",[1736,25395,25396],{"class":2674},"fill",[1736,25398,7751],{"class":1912},[1736,25400,25401],{"class":1935},"'Garfield'",[1736,25403,16106],{"class":1912},[1736,25405,25406,25408,25410,25412,25414,25416,25418,25420,25422,25425,25427,25430],{"class":1738,"line":2353},[1736,25407,25323],{"class":4866},[1736,25409,22665],{"class":1912},[1736,25411,1032],{"class":2674},[1736,25413,7751],{"class":1912},[1736,25415,25385],{"class":1935},[1736,25417,10685],{"class":1912},[1736,25419,25390],{"class":1935},[1736,25421,25393],{"class":1912},[1736,25423,25424],{"class":2674},"press",[1736,25426,7751],{"class":1912},[1736,25428,25429],{"class":1935},"'Enter'",[1736,25431,16106],{"class":1912},[1736,25433,25434],{"class":1738,"line":2358},[1736,25435,1747],{"emptyLinePlaceholder":790},[1736,25437,25438],{"class":1738,"line":2364},[1736,25439,25440],{"class":6820},"  // Verify that 'The Garfield Movie' appears in the search results\n",[1736,25442,25443,25445,25448,25451,25453,25455,25458,25460,25463,25466,25468,25471,25474],{"class":1738,"line":2370},[1736,25444,25323],{"class":4866},[1736,25446,25447],{"class":2674}," expect",[1736,25449,25450],{"class":1912},"(page.",[1736,25452,1032],{"class":2674},[1736,25454,7751],{"class":1912},[1736,25456,25457],{"class":1935},"'heading'",[1736,25459,10685],{"class":1912},[1736,25461,25462],{"class":1935},"'The Garfield Movie'",[1736,25464,25465],{"class":1912},", level: ",[1736,25467,10820],{"class":1918},[1736,25469,25470],{"class":1912}," })).",[1736,25472,25473],{"class":2674},"toBeVisible",[1736,25475,15752],{"class":1912},[1736,25477,25478],{"class":1738,"line":2376},[1736,25479,17657],{"class":1912},[11,25481,25482],{},"🔁 What’s Next?\nFrom here, you can:",[70,25484,25485,25488,25491],{},[73,25486,25487],{},"Review or refine the test",[73,25489,25490],{},"Use GitHub MCP to open a pull request",[73,25492,25493],{},"Merge it into your test suite",[11,25495,25496],{},"And remember this all happened without needing access to the codebase.",[11,25498,25499],{},"💡 Why This Matters\nThis workflow is a major win for:",[70,25501,25502,25505,25508,25511],{},[73,25503,25504],{},"QA testers in black-box environments",[73,25506,25507],{},"Agencies testing external or client-owned websites",[73,25509,25510],{},"Regulated industries with restricted code access",[73,25512,25513],{},"Developers wanting to speed up test creation with natural language",[11,25515,25516,25517],{},"🎬 Watch It in Action\nWant to see the full demo? Watch the YouTube video and experience how easy it is to generate reliable Playwright tests with the Playwright MCP and Copilot: ",[15,25518,25521],{"href":25519,"rel":25520},"https://youtu.be/AaCj939XIQ4",[19],"Watch the Demo",[11,25523,25524],{},"🎉 Final Thoughts\nThe combination of Playwright, Copilot, and the Model Context Protocol (MCP) unlocks a new level of intelligent automation.",[11,25526,25527],{},"Go give it a try. Happy testing!",[731,25529],{},[11,25531,25532],{},[133,25533,25534,25535,891],{},"This post was originally published on ",[15,25536,25539],{"href":25537,"rel":25538},"https://dev.to/debs_obrien/generate-playwright-tests-without-code-access-using-mcp-and-copilot-2m05",[19],"DEV.to",[2011,25541,25542],{},"html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":25544},[25545,25546,25547,25550],{"id":25099,"depth":748,"text":25100},{"id":25117,"depth":748,"text":25118},{"id":25223,"depth":748,"text":25224,"children":25548},[25549],{"id":25234,"depth":756,"text":25235},{"id":25259,"depth":748,"text":25260},"2025-06-18","Discover how to use MCP (Model Context Protocol) and GitHub Copilot to generate Playwright tests without needing access to your application's source code, perfect for testing external sites or black-box testing scenarios.",{"loading":3458},"/blog/generate-playwright-tests-without-code-access",{"title":25080,"description":25552},"blog/generate-playwright-tests-without-code-access",[1412,1411,3321,795],"8-ntqna0zEMn7BEbT9wkybLxaQ6F3Fi9KStmj1dqamo",{"id":25560,"title":25561,"body":25562,"canonical":788,"date":23734,"description":25835,"extension":786,"featured":787,"image":25836,"meta":25837,"navigation":790,"ogimage":788,"path":25839,"provider":3460,"published":790,"seo":25840,"stem":25841,"tags":25842,"url":788,"__hash__":25843},"blog/blog/getting-started-with-playwright-testing.md","Getting Started with Playwright Testing",{"type":8,"value":25563,"toc":25825},[25564,25567,25570,25573,25579,25582,25585,25588,25592,25595,25631,25635,25638,25644,25654,25660,25663,25669,25673,25676,25694,25697,25701,25709,25723,25726,25734,25738,25741,25744,25748,25751,25754,25766,25769,25783,25791,25793,25796,25803,25805,25822],[11,25565,25566],{},"Nowadays people really do want to write tests. They know they want to, they know the need to. We have gone past the making a point of how tests can save time. Developers know this yet they are not testing their apps. Why is this?",[11,25568,25569],{},"Mainly it is because developers do not have the time. They need time to learn the testing Framework and they need time to write the tests and let's face it most developers are busy and have to reach important deadlines and so testing gets left for some time in the future when they are not so busy. And unfortunately sometimes that time in the future just never comes. That means that developers are often faced with fixing bugs that could have been prevented if tests had been created from the early stages.",[11,25571,25572],{},"So what if we could make it easier for developers, soften the learning curve when it comes to writing tests so they can at least get started, and then help them build their tests along the way without just telling them to go read the docs. This is exactly what Playwright has done.",[11,25574,25575],{},[121,25576],{"alt":25577,"src":25578},"browser window with shoe store app and another window showing tests being generated","https://res.cloudinary.com/debsobrien/image/upload/v1648106423/debbie.codes/blog/2022/codegen_2x_psrpgk.png",[11,25580,25581],{},"Playwright is an Open Source Testing Tool, maintained by Microsoft, and is completely free to use. It enables reliable end-to-end testing for modern web apps. It supports multi languages including JavaScript, TypeScript, Python, Java and .NET.",[11,25583,25584],{},"Tests are run across all modern rendering engines including Chromium, WebKit, and Firefox and you can test on Windows, Linux, and macOS, locally or on CI, headless or headed and you can even test a Native mobile emulation of Google Chrome for Android and Mobile Safari. The same rendering engine works on your Desktop and in the Cloud. Sounds pretty cool right!",[11,25586,25587],{},"But how easy is it to get started? Let me show you.",[23,25589,25591],{"id":25590},"installing-playwright","Installing Playwright",[11,25593,25594],{},"You can run this script in an already existing project or you can create a new project.",[299,25596,25598],{"className":2665,"code":25597,"language":2667,"meta":307,"style":307},"# Run from your project's root directory\nnpm init playwright@latest\n# Or create a new project\nnpm init playwright@latest new-project\n",[179,25599,25600,25605,25614,25619],{"__ignoreMap":307},[1736,25601,25602],{"class":1738,"line":1739},[1736,25603,25604],{"class":6820},"# Run from your project's root directory\n",[1736,25606,25607,25609,25611],{"class":1738,"line":748},[1736,25608,6565],{"class":2674},[1736,25610,15504],{"class":1935},[1736,25612,25613],{"class":1935}," playwright@latest\n",[1736,25615,25616],{"class":1738,"line":756},[1736,25617,25618],{"class":6820},"# Or create a new project\n",[1736,25620,25621,25623,25625,25628],{"class":1738,"line":1755},[1736,25622,6565],{"class":2674},[1736,25624,15504],{"class":1935},[1736,25626,25627],{"class":1935}," playwright@latest",[1736,25629,25630],{"class":1935}," new-project\n",[23,25632,25634],{"id":25633},"installing-playwright-with-vs-code-extension","Installing Playwright with VS Code Extension",[11,25636,25637],{},"Alternatively my favorite way to get started is by using the VS Code extension to create a new project. In VSCode open the extensions and search for Playwright. Make sure you choose the one that is verified by Microsoft.",[11,25639,25640],{},[121,25641],{"alt":25642,"src":25643},"vs code extension for playwright","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648065035/debbie.codes/blog/2022/vscode-playwright_mqzmnu.png",[11,25645,25646,25647,25650,25651,891],{},"Once the extension is installed you can then install Playwright in your project using the command bar in VS Code. Use ",[179,25648,25649],{},"CMD+Shift+P"," and then type ",[179,25652,25653],{},">playwright",[11,25655,25656],{},[121,25657],{"alt":25658,"src":25659},"command bar in vs code ","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648065351/debbie.codes/blog/2022/vscode-ext1_g7xogh.png",[11,25661,25662],{},"You can then choose if you want the tests to run on Chromium, WebKit or Firefox and you can also decide if you want to add a GitHub action. You can always add or remove these later on through the Playwright config file.",[11,25664,25665],{},[121,25666],{"alt":25667,"src":25668},"dropdown menu of browsers available","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648065599/debbie.codes/blog/2022/choose-browser_pdxd4o.png",[23,25670,25672],{"id":25671},"what-you-get-after-installing","What you get after Installing",[11,25674,25675],{},"Once installed you will get:",[70,25677,25678,25685,25691],{},[73,25679,25680,25682,25683,891],{},[179,25681,17364],{}," file, unless you are already inside a project that contains this file and if so Playwright will be added to your ",[179,25684,17364],{},[73,25686,25687,25690],{},[179,25688,25689],{},"playwright.config.ts"," file which is where you can configure Playwright, add or remove the browsers you want to test on and so much more.",[73,25692,25693],{},"Tests folder with an example test file.",[11,25695,25696],{},"The example test file is a very complete example of how to write tests for a TODO app and I highly encourage you to check it out. But I want to show you how to get started writing your own tests so lets continue with that.",[23,25698,25700],{"id":25699},"generating-tests-with-codegen","Generating Tests with Codegen",[11,25702,25703,25704,25708],{},"We will use ",[15,25705,25707],{"href":22733,"rel":25706},[19],"codegen"," which generates tests by recording your actions. You can literally open any website and start browsing your application and interacting with it and as you do your tests will literally be written before you very own eyes. To open Codegen run the following command:",[299,25710,25712],{"className":2665,"code":25711,"language":2667,"meta":307,"style":307},"npm playwright codegen\n",[179,25713,25714],{"__ignoreMap":307},[1736,25715,25716,25718,25720],{"class":1738,"line":1739},[1736,25717,6565],{"class":2674},[1736,25719,22593],{"class":1935},[1736,25721,25722],{"class":1935}," codegen\n",[11,25724,25725],{},"This will open two windows for you. The first one is the browser window that you will use to navigate to your site that you want to test and the second window is the where the test code will be saved. Check out the video to see it in action.",[22564,25727,5302,25728,5302,25731,22574],{"width":6371,"height":6371,"controls":790},[5304,25729],{"src":25730,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648066315/debbie.codes/blog/2022/playwright-codegen_drluzl.mp4",[5304,25732],{"src":25733,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648066315/debbie.codes/blog/2022/playwright-codegen_drluzl.ogg",[23,25735,25737],{"id":25736},"copy-the-tests-to-your-editor","Copy the Tests to your Editor",[11,25739,25740],{},"Once you have finished recording your actions you can copy the test code to a test file in VS Code and there you go, you have just written your first test. If you prefer your tests in another language then choose from the dropdown menu and the the code will change to that language.",[11,25742,25743],{},"You can then click the copy button to copy the code to your clipboard and paste it into a newly created test file in your tests folder. And there you have it. You have just written your first test and all you had to do was click around your site and copy and paste the code. Now how cool is that.",[23,25745,25747],{"id":25746},"running-your-tests","Running your Tests",[11,25749,25750],{},"If you now want to run your test and are using the VS Code extension then you can press the green triangle next to the line where your tests start.",[11,25752,25753],{},"Or you can run the tests by running the following command in your terminal:",[299,25755,25756],{"className":2665,"code":22584,"language":2667,"meta":307,"style":307},[179,25757,25758],{"__ignoreMap":307},[1736,25759,25760,25762,25764],{"class":1738,"line":1739},[1736,25761,2675],{"class":2674},[1736,25763,22593],{"class":1935},[1736,25765,22596],{"class":1935},[11,25767,25768],{},"This will run the test and give you the output in the cli. If you want to run your tests in a headed manner, meaning it will open your browser so you can visually see it run the tests, then you can use the following command:",[299,25770,25771],{"className":2665,"code":22602,"language":2667,"meta":307,"style":307},[179,25772,25773],{"__ignoreMap":307},[1736,25774,25775,25777,25779,25781],{"class":1738,"line":1739},[1736,25776,2675],{"class":2674},[1736,25778,22593],{"class":1935},[1736,25780,22613],{"class":1935},[1736,25782,22616],{"class":1918},[11,25784,25785,25786,25790],{},"Be wared though as the tests are super fast meaning it will open, run the tests and close in a matter of seconds. In a future post I will show you how to pause the tests, debug the tests or open a trace file or feel free to check out the ",[15,25787,22728],{"href":25788,"rel":25789},"https://playwright.dev/docs/inspector",[19]," for more information.",[23,25792,3294],{"id":3293},[11,25794,25795],{},"Playwright is super fun and easy to get started with and Codegen is an amazing tool that is going to really help make developers lives easier. Now there really is no excuses for not testing your applications. I highly encourage you to check it out and take it for a spin.",[11,25797,25798,25799,25802],{},"Of course automated code might not always be perfect and may need tweaking but it sure is a great way of getting started and getting your tests written in record time. As always check out the ",[15,25800,22728],{"href":22726,"rel":25801},[19]," to learn more and further enhance your tests.",[23,25804,5706],{"id":5705},[70,25806,25807,25812,25817],{},[73,25808,25809],{},[15,25810,22728],{"href":22726,"rel":25811},[19],[73,25813,25814],{},[15,25815,22735],{"href":22733,"rel":25816},[19],[73,25818,25819],{},[15,25820,22742],{"href":22740,"rel":25821},[19],[2011,25823,25824],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":25826},[25827,25828,25829,25830,25831,25832,25833,25834],{"id":25590,"depth":748,"text":25591},{"id":25633,"depth":748,"text":25634},{"id":25671,"depth":748,"text":25672},{"id":25699,"depth":748,"text":25700},{"id":25736,"depth":748,"text":25737},{"id":25746,"depth":748,"text":25747},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"How can we get more developers to write tests? We can create better tools that makes testing easier. Let's take a look at Playwright and it's amazing features including codegen which writes your tests for you.","v1648106423/debbie.codes/blog/2022/codegen_2x_psrpgk.png",{"ogImage":25838},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648106423/debbie.codes/blog/2022/codegen_2x_psrpgk.png","/blog/getting-started-with-playwright-testing",{"title":25561,"description":25835},"blog/getting-started-with-playwright-testing",[1411,1412],"AD2fCFrUqoyE-p74byTIjgWnB_tk24Uf-Yyts0i06j4",{"id":25845,"title":25846,"body":25847,"canonical":788,"date":26311,"description":25851,"extension":786,"featured":787,"image":26312,"meta":26313,"navigation":790,"ogimage":788,"path":26315,"provider":3460,"published":790,"seo":26316,"stem":26317,"tags":26318,"url":788,"__hash__":26321},"blog/blog/github-mcp-server.md","Setting Up the Official GitHub MCP Server, A simple Guide",{"type":8,"value":25848,"toc":26299},[25849,25852,25856,25864,25868,25879,25881,25884,25903,25907,25910,25914,25922,25926,25929,25944,26138,26142,26145,26259,26261,26264,26267,26271,26274,26296],[11,25850,25851],{},"The GitHub MCP (Model Context Protocol) Server is a powerful tool that provides seamless integration with GitHub APIs, enabling advanced automation and interaction capabilities. This guide will walk you through how to set up and use the official GitHub MCP Server.",[23,25853,25855],{"id":25854},"what-is-the-github-mcp-server","What is the GitHub MCP Server?",[11,25857,25858,25859,25863],{},"The GitHub MCP Server implements the ",[15,25860,5800],{"href":25861,"rel":25862},"https://modelcontextprotocol.io/introduction",[19],", allowing AI tools and applications to interact with GitHub's ecosystem. This server acts as a bridge between AI tools and GitHub's APIs, making it easier to automate workflows, extract data, and build powerful integrations.",[23,25865,25867],{"id":25866},"use-cases","Use Cases",[70,25869,25870,25873,25876],{},[73,25871,25872],{},"Automating GitHub workflows and processes",[73,25874,25875],{},"Extracting and analyzing data from GitHub repositories",[73,25877,25878],{},"Building AI-powered tools that interact with GitHub's ecosystem",[23,25880,15453],{"id":15452},[11,25882,25883],{},"Before getting started, make sure you have:",[2260,25885,25886,25894],{},[73,25887,25888,25893],{},[15,25889,25892],{"href":25890,"rel":25891},"https://www.docker.com/",[19],"Docker"," installed and running on your system",[73,25895,25896,25897,25902],{},"A ",[15,25898,25901],{"href":25899,"rel":25900},"https://github.com/settings/personal-access-tokens/new",[19],"GitHub Personal Access Token"," with appropriate permissions for the GitHub APIs you plan to use",[23,25904,25906],{"id":25905},"installation-options","Installation Options",[11,25908,25909],{},"There are several ways to set up the GitHub MCP Server:",[138,25911,25913],{"id":25912},"option-1-one-click-installation-for-vs-code","Option 1: One-Click Installation for VS Code",[11,25915,25916,25917,891],{},"The easiest way to get started is using the one-click installation buttons that ",[15,25918,25921],{"href":25919,"rel":25920},"https://github.com/github/github-mcp-server",[19],"GitHub provides in the repository README",[138,25923,25925],{"id":25924},"option-2-manual-configuration-in-vs-code","Option 2: Manual Configuration in VS Code",[11,25927,25928],{},"To manually set up the GitHub MCP Server in VS Code:",[2260,25930,25931,25938,25941],{},[73,25932,25933,25934,25937],{},"Open VS Code and press ",[179,25935,25936],{},"Ctrl + Shift + P"," to open the command palette",[73,25939,25940],{},"Type \"Preferences: Open User Settings (JSON)\" and select it",[73,25942,25943],{},"Add the following JSON configuration:",[299,25945,25947],{"className":1903,"code":25946,"language":1905,"meta":307,"style":307},"{\n  \"mcp\": {\n    \"inputs\": [\n      {\n        \"type\": \"promptString\",\n        \"id\": \"github_token\",\n        \"description\": \"GitHub Personal Access Token\",\n        \"password\": true\n      }\n    ],\n    \"servers\": {\n      \"github\": {\n        \"command\": \"docker\",\n        \"args\": [\n          \"run\",\n          \"-i\",\n          \"--rm\",\n          \"-e\",\n          \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n          \"ghcr.io/github/github-mcp-server\"\n        ],\n        \"env\": {\n          \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"\u003CYOUR_TOKEN>\"\n        }\n      }\n    }\n  }\n}\n",[179,25948,25949,25953,25960,25967,25971,25983,25995,26007,26016,26020,26024,26031,26038,26050,26057,26064,26071,26078,26085,26092,26097,26101,26108,26117,26122,26126,26130,26134],{"__ignoreMap":307},[1736,25950,25951],{"class":1738,"line":1739},[1736,25952,1913],{"class":1912},[1736,25954,25955,25958],{"class":1738,"line":748},[1736,25956,25957],{"class":1918},"  \"mcp\"",[1736,25959,1922],{"class":1912},[1736,25961,25962,25965],{"class":1738,"line":756},[1736,25963,25964],{"class":1918},"    \"inputs\"",[1736,25966,1930],{"class":1912},[1736,25968,25969],{"class":1738,"line":1755},[1736,25970,15683],{"class":1912},[1736,25972,25973,25976,25978,25981],{"class":1738,"line":1761},[1736,25974,25975],{"class":1918},"        \"type\"",[1736,25977,3065],{"class":1912},[1736,25979,25980],{"class":1935},"\"promptString\"",[1736,25982,1939],{"class":1912},[1736,25984,25985,25988,25990,25993],{"class":1738,"line":1767},[1736,25986,25987],{"class":1918},"        \"id\"",[1736,25989,3065],{"class":1912},[1736,25991,25992],{"class":1935},"\"github_token\"",[1736,25994,1939],{"class":1912},[1736,25996,25997,26000,26002,26005],{"class":1738,"line":1772},[1736,25998,25999],{"class":1918},"        \"description\"",[1736,26001,3065],{"class":1912},[1736,26003,26004],{"class":1935},"\"GitHub Personal Access Token\"",[1736,26006,1939],{"class":1912},[1736,26008,26009,26012,26014],{"class":1738,"line":1778},[1736,26010,26011],{"class":1918},"        \"password\"",[1736,26013,3065],{"class":1912},[1736,26015,21380],{"class":1918},[1736,26017,26018],{"class":1738,"line":1784},[1736,26019,14448],{"class":1912},[1736,26021,26022],{"class":1738,"line":1790},[1736,26023,1949],{"class":1912},[1736,26025,26026,26029],{"class":1738,"line":1796},[1736,26027,26028],{"class":1918},"    \"servers\"",[1736,26030,1922],{"class":1912},[1736,26032,26033,26036],{"class":1738,"line":2353},[1736,26034,26035],{"class":1918},"      \"github\"",[1736,26037,1922],{"class":1912},[1736,26039,26040,26043,26045,26048],{"class":1738,"line":2358},[1736,26041,26042],{"class":1918},"        \"command\"",[1736,26044,3065],{"class":1912},[1736,26046,26047],{"class":1935},"\"docker\"",[1736,26049,1939],{"class":1912},[1736,26051,26052,26055],{"class":1738,"line":2364},[1736,26053,26054],{"class":1918},"        \"args\"",[1736,26056,1930],{"class":1912},[1736,26058,26059,26062],{"class":1738,"line":2370},[1736,26060,26061],{"class":1935},"          \"run\"",[1736,26063,1939],{"class":1912},[1736,26065,26066,26069],{"class":1738,"line":2376},[1736,26067,26068],{"class":1935},"          \"-i\"",[1736,26070,1939],{"class":1912},[1736,26072,26073,26076],{"class":1738,"line":2381},[1736,26074,26075],{"class":1935},"          \"--rm\"",[1736,26077,1939],{"class":1912},[1736,26079,26080,26083],{"class":1738,"line":2387},[1736,26081,26082],{"class":1935},"          \"-e\"",[1736,26084,1939],{"class":1912},[1736,26086,26087,26090],{"class":1738,"line":2393},[1736,26088,26089],{"class":1935},"          \"GITHUB_PERSONAL_ACCESS_TOKEN\"",[1736,26091,1939],{"class":1912},[1736,26093,26094],{"class":1738,"line":2398},[1736,26095,26096],{"class":1935},"          \"ghcr.io/github/github-mcp-server\"\n",[1736,26098,26099],{"class":1738,"line":2404},[1736,26100,15903],{"class":1912},[1736,26102,26103,26106],{"class":1738,"line":6959},[1736,26104,26105],{"class":1918},"        \"env\"",[1736,26107,1922],{"class":1912},[1736,26109,26110,26112,26114],{"class":1738,"line":7296},[1736,26111,26089],{"class":1918},[1736,26113,3065],{"class":1912},[1736,26115,26116],{"class":1935},"\"\u003CYOUR_TOKEN>\"\n",[1736,26118,26119],{"class":1738,"line":7305},[1736,26120,26121],{"class":1912},"        }\n",[1736,26123,26124],{"class":1738,"line":7310},[1736,26125,14448],{"class":1912},[1736,26127,26128],{"class":1738,"line":9659},[1736,26129,9853],{"class":1912},[1736,26131,26132],{"class":1738,"line":9680},[1736,26133,1971],{"class":1912},[1736,26135,26136],{"class":1738,"line":9699},[1736,26137,1976],{"class":1912},[138,26139,26141],{"id":26140},"option-3-using-with-claude-desktop","Option 3: Using with Claude Desktop",[11,26143,26144],{},"To use the GitHub MCP Server with Claude Desktop, add the following configuration:",[299,26146,26148],{"className":1903,"code":26147,"language":1905,"meta":307,"style":307},"{\n  \"mcpServers\": {\n    \"github\": {\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\",\n        \"-i\",\n        \"--rm\",\n        \"-e\",\n        \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n        \"ghcr.io/github/github-mcp-server\"\n      ],\n      \"env\": {\n        \"GITHUB_PERSONAL_ACCESS_TOKEN\": \"\u003CYOUR_TOKEN>\"\n      }\n    }\n  }\n}\n",[179,26149,26150,26154,26160,26167,26177,26183,26190,26197,26204,26211,26218,26223,26228,26235,26243,26247,26251,26255],{"__ignoreMap":307},[1736,26151,26152],{"class":1738,"line":1739},[1736,26153,1913],{"class":1912},[1736,26155,26156,26158],{"class":1738,"line":748},[1736,26157,17036],{"class":1918},[1736,26159,1922],{"class":1912},[1736,26161,26162,26165],{"class":1738,"line":756},[1736,26163,26164],{"class":1918},"    \"github\"",[1736,26166,1922],{"class":1912},[1736,26168,26169,26171,26173,26175],{"class":1738,"line":1755},[1736,26170,17050],{"class":1918},[1736,26172,3065],{"class":1912},[1736,26174,26047],{"class":1935},[1736,26176,1939],{"class":1912},[1736,26178,26179,26181],{"class":1738,"line":1761},[1736,26180,17062],{"class":1918},[1736,26182,1930],{"class":1912},[1736,26184,26185,26188],{"class":1738,"line":1767},[1736,26186,26187],{"class":1935},"        \"run\"",[1736,26189,1939],{"class":1912},[1736,26191,26192,26195],{"class":1738,"line":1772},[1736,26193,26194],{"class":1935},"        \"-i\"",[1736,26196,1939],{"class":1912},[1736,26198,26199,26202],{"class":1738,"line":1778},[1736,26200,26201],{"class":1935},"        \"--rm\"",[1736,26203,1939],{"class":1912},[1736,26205,26206,26209],{"class":1738,"line":1784},[1736,26207,26208],{"class":1935},"        \"-e\"",[1736,26210,1939],{"class":1912},[1736,26212,26213,26216],{"class":1738,"line":1790},[1736,26214,26215],{"class":1935},"        \"GITHUB_PERSONAL_ACCESS_TOKEN\"",[1736,26217,1939],{"class":1912},[1736,26219,26220],{"class":1738,"line":1796},[1736,26221,26222],{"class":1935},"        \"ghcr.io/github/github-mcp-server\"\n",[1736,26224,26225],{"class":1738,"line":2353},[1736,26226,26227],{"class":1912},"      ],\n",[1736,26229,26230,26233],{"class":1738,"line":2358},[1736,26231,26232],{"class":1918},"      \"env\"",[1736,26234,1922],{"class":1912},[1736,26236,26237,26239,26241],{"class":1738,"line":2364},[1736,26238,26215],{"class":1918},[1736,26240,3065],{"class":1912},[1736,26242,26116],{"class":1935},[1736,26244,26245],{"class":1738,"line":2370},[1736,26246,14448],{"class":1912},[1736,26248,26249],{"class":1738,"line":2376},[1736,26250,9853],{"class":1912},[1736,26252,26253],{"class":1738,"line":2381},[1736,26254,1971],{"class":1912},[1736,26256,26257],{"class":1738,"line":2387},[1736,26258,1976],{"class":1912},[23,26260,3294],{"id":3293},[11,26262,26263],{},"The GitHub MCP Server provides a powerful way to integrate AI tools with GitHub's ecosystem. Open Copilot in VS Code and choose agent mode and simply ask it to create an issue on one of your repos, or list the open issues or get the amount of stars. Play around with it and see what you can do!",[11,26265,26266],{},"Remember to keep your personal access token secure and to only grant the permissions necessary for your use case.",[23,26268,26270],{"id":26269},"additional-resources","Additional Resources",[11,26272,26273],{},"Check out the following videos on the Playwright MCP Server:",[70,26275,26276,26283,26290],{},[73,26277,26278],{},[15,26279,26282],{"href":26280,"rel":26281},"https://youtu.be/r3PzUlbfMXA?si=5g2luSzvTyppcOR2",[19],"AI Goes Shopping for You with Playwright's MCP Server!",[73,26284,26285],{},[15,26286,26289],{"href":26287,"rel":26288},"https://youtu.be/cifdyJkKs04?si=BcXNHQe-tRIfxdaF",[19],"Install Playwright MCP Server in VS Code – AI-Powered Web Automation!",[73,26291,26292],{},[15,26293,26289],{"href":26294,"rel":26295},"https://youtu.be/cifdyJkKs04?si=CfMk5y2hY3MFlfL6",[19],[2011,26297,26298],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":26300},[26301,26302,26303,26304,26309,26310],{"id":25854,"depth":748,"text":25855},{"id":25866,"depth":748,"text":25867},{"id":15452,"depth":748,"text":15453},{"id":25905,"depth":748,"text":25906,"children":26305},[26306,26307,26308],{"id":25912,"depth":756,"text":25913},{"id":25924,"depth":756,"text":25925},{"id":26140,"depth":756,"text":26141},{"id":3293,"depth":748,"text":3294},{"id":26269,"depth":748,"text":26270},"2025-04-16","v1648106423/debbie.codes/blog/2025/GitHub_MCP_server_2x_1_jizwlw",{"ogImage":26314},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648106423/debbie.codes/blog/2025/GitHub_MCP_server_2x_1_jizwlw","/blog/github-mcp-server",{"title":25846,"description":25851},"blog/github-mcp-server",[26319,26320],"MCP","AI","rV9w4M9YQOkEh3WlzlqSU4DIyLQMORAdlmVOZnSczps",{"id":26323,"title":26324,"body":26325,"canonical":788,"date":26329,"description":26330,"extension":786,"featured":787,"image":26331,"meta":26332,"navigation":790,"ogimage":788,"path":26333,"provider":3460,"published":787,"seo":26334,"stem":26335,"tags":26336,"url":26337,"__hash__":26338},"blog/blog/going-dark-nuxt-color-mode.md","Going dark with Nuxt.js color mode",{"type":8,"value":26326,"toc":26327},[],{"title":307,"searchDepth":748,"depth":748,"links":26328},[],"2020-05-19","The @nuxtjs/color-mode module is a cool way of adding dark mode to your site. But not only does it switch from dark to light but also any color theme (eg sepia mode). It even has auto detection so that it will choose the right mode depending on your system appearance.","v1630862640/debbie.codes/featured-posts/going-dark-mode_jizwlw",{"platform":20935},"/blog/going-dark-nuxt-color-mode",{"title":26324,"description":26330},"blog/going-dark-nuxt-color-mode",[5239],"https://nuxtjs.org/blog/going-dark-with-nuxtjs-color-mode","UZ_BWIXrCF5TSUm_WMGifi6U0G_aPbK4cnvQ5-G-JYI",{"id":799,"title":800,"body":26340,"canonical":1403,"date":1404,"description":1405,"extension":786,"featured":787,"image":788,"meta":26738,"navigation":790,"ogimage":788,"path":1407,"provider":788,"published":790,"seo":26739,"stem":1409,"tags":26740,"url":788,"__hash__":1413},{"type":8,"value":26341,"toc":26717},[26342,26344,26346,26350,26352,26354,26366,26368,26370,26372,26376,26378,26420,26422,26424,26426,26444,26446,26450,26452,26454,26484,26496,26498,26500,26502,26514,26518,26530,26532,26534,26539,26543,26545,26551,26553,26555,26559,26563,26565,26567,26589,26591,26593,26595,26597,26599,26665,26667,26677,26679,26683,26697,26701],[11,26343,805],{},[11,26345,808],{},[11,26347,811,26348,815],{},[179,26349,814],{},[23,26351,819],{"id":818},[11,26353,822],{},[11,26355,825,26356,829,26358,829,26360,829,26362,608,26364],{},[58,26357,828],{},[58,26359,832],{},[58,26361,835],{},[58,26363,838],{},[58,26365,841],{},[11,26367,844],{},[23,26369,848],{"id":847},[11,26371,851],{},[11,26373,854,26374,858],{},[133,26375,857],{},[11,26377,861],{},[70,26379,26380,26384,26388,26392,26398,26402,26410,26414],{},[73,26381,26382,869],{},[58,26383,868],{},[73,26385,26386,875],{},[58,26387,874],{},[73,26389,26390,881],{},[58,26391,880],{},[73,26393,26394,887,26396,891],{},[58,26395,886],{},[179,26397,890],{},[73,26399,26400,897],{},[58,26401,896],{},[73,26403,26404,903,26406,907,26408,911],{},[58,26405,902],{},[179,26407,906],{},[179,26409,910],{},[73,26411,26412,917],{},[58,26413,916],{},[73,26415,26416,923,26418,927],{},[58,26417,922],{},[179,26419,926],{},[11,26421,930],{},[138,26423,934],{"id":933},[11,26425,937],{},[70,26427,26428,26436,26440,26442],{},[73,26429,942,26430,907,26432,907,26434,952],{},[179,26431,945],{},[179,26433,948],{},[179,26435,951],{},[73,26437,955,26438,959],{},[179,26439,958],{},[73,26441,962],{},[73,26443,965],{},[23,26445,969],{"id":968},[11,26447,972,26448,891],{},[179,26449,975],{},[138,26451,979],{"id":978},[11,26453,982],{},[70,26455,26456,26460,26464,26468,26472,26476,26480],{},[73,26457,26458,990],{},[58,26459,989],{},[73,26461,26462,996],{},[58,26463,995],{},[73,26465,26466,1002],{},[58,26467,1001],{},[73,26469,26470,1008],{},[58,26471,1007],{},[73,26473,26474,1014],{},[58,26475,1013],{},[73,26477,26478,1020],{},[58,26479,1019],{},[73,26481,26482,1026],{},[58,26483,1025],{},[11,26485,1029,26486,1033,26488,1033,26490,1040,26492,1044,26494,1048],{},[179,26487,1032],{},[179,26489,1036],{},[179,26491,1039],{},[179,26493,1043],{},[179,26495,1047],{},[11,26497,1051],{},[23,26499,1055],{"id":1054},[138,26501,1059],{"id":1058},[11,26503,1062,26504,1066,26506,1070,26508,829,26510,829,26512,1080],{},[179,26505,1065],{},[179,26507,1069],{},[179,26509,1073],{},[179,26511,1076],{},[179,26513,1079],{},[11,26515,1083,26516,1087],{},[58,26517,1086],{},[70,26519,26520,26526],{},[73,26521,26522,1095,26524,1099],{},[58,26523,1094],{},[179,26525,1098],{},[73,26527,26528,1105],{},[58,26529,1104],{},[138,26531,1109],{"id":1108},[11,26533,1112],{},[299,26535,26537],{"className":26536,"code":1116,"language":304},[302],[179,26538,1116],{"__ignoreMap":307},[11,26540,1121,26541,1125],{},[179,26542,1124],{},[138,26544,1129],{"id":1128},[11,26546,1132,26547,1136,26549,1139],{},[179,26548,1135],{},[179,26550,1065],{},[23,26552,1143],{"id":1142},[11,26554,1146],{},[11,26556,26557,1152],{},[58,26558,1151],{},[11,26560,26561,1158],{},[58,26562,1157],{},[23,26564,1162],{"id":1161},[11,26566,1165],{},[70,26568,26569,26573,26577,26581,26585],{},[73,26570,26571,1173],{},[58,26572,1172],{},[73,26574,26575,1179],{},[58,26576,1178],{},[73,26578,26579,1185],{},[58,26580,1184],{},[73,26582,26583,1191],{},[58,26584,1190],{},[73,26586,26587,1197],{},[58,26588,1196],{},[138,26590,1201],{"id":1200},[11,26592,1204],{},[11,26594,1207],{},[11,26596,1210],{},[23,26598,1214],{"id":1213},[1216,26600,26601,26613],{},[1219,26602,26603],{},[1222,26604,26605,26607,26609,26611],{},[1225,26606,1227],{},[1225,26608,1230],{},[1225,26610,1233],{},[1225,26612,1236],{},[1238,26614,26615,26625,26635,26645,26655],{},[1222,26616,26617,26619,26621,26623],{},[1243,26618,1245],{},[1243,26620,1248],{},[1243,26622,1251],{},[1243,26624,1254],{},[1222,26626,26627,26629,26631,26633],{},[1243,26628,1259],{},[1243,26630,1262],{},[1243,26632,1265],{},[1243,26634,1268],{},[1222,26636,26637,26639,26641,26643],{},[1243,26638,1273],{},[1243,26640,1248],{},[1243,26642,1265],{},[1243,26644,1280],{},[1222,26646,26647,26649,26651,26653],{},[1243,26648,1285],{},[1243,26650,1248],{},[1243,26652,1290],{},[1243,26654,1293],{},[1222,26656,26657,26659,26661,26663],{},[1243,26658,1298],{},[1243,26660,1301],{},[1243,26662,1301],{},[1243,26664,1306],{},[23,26666,1310],{"id":1309},[70,26668,26669,26671,26673,26675],{},[73,26670,1315],{},[73,26672,1318],{},[73,26674,1321],{},[73,26676,1324],{},[23,26678,1328],{"id":1327},[11,26680,26681],{},[58,26682,1333],{},[70,26684,26685,26687,26689,26693],{},[73,26686,1338],{},[73,26688,1341],{},[73,26690,1344,26691,1348],{},[179,26692,1347],{},[73,26694,1351,26695],{},[179,26696,1065],{},[11,26698,26699],{},[58,26700,1358],{},[70,26702,26703,26705,26707,26709,26711,26713,26715],{},[73,26704,1363],{},[73,26706,1366],{},[73,26708,1369],{},[73,26710,1372],{},[73,26712,1375],{},[73,26714,1378],{},[73,26716,1381],{},{"title":307,"searchDepth":748,"depth":748,"links":26718},[26719,26720,26723,26726,26731,26732,26735,26736,26737],{"id":818,"depth":748,"text":819},{"id":847,"depth":748,"text":848,"children":26721},[26722],{"id":933,"depth":756,"text":934},{"id":968,"depth":748,"text":969,"children":26724},[26725],{"id":978,"depth":756,"text":979},{"id":1054,"depth":748,"text":1055,"children":26727},[26728,26729,26730],{"id":1058,"depth":756,"text":1059},{"id":1108,"depth":756,"text":1109},{"id":1128,"depth":756,"text":1129},{"id":1142,"depth":748,"text":1143},{"id":1161,"depth":748,"text":1162,"children":26733},[26734],{"id":1200,"depth":756,"text":1201},{"id":1213,"depth":748,"text":1214},{"id":1309,"depth":748,"text":1310},{"id":1327,"depth":748,"text":1328},{},{"title":800,"description":1405},[1411,1412,795],{"id":26742,"title":26743,"body":26744,"canonical":26794,"date":26795,"description":26796,"extension":786,"featured":787,"image":788,"meta":26797,"navigation":790,"ogimage":788,"path":26798,"provider":788,"published":787,"seo":26799,"stem":26800,"tags":26801,"url":788,"__hash__":26802},"blog/blog/how-i-vibe-code-improving-my-site-design-with-goose-and-gemini-3.md","How I vibe code: Improving my site design with Goose and Gemini 3",{"type":8,"value":26745,"toc":26792},[26746,26749,26752,26755,26758,26761,26764,26767,26772,26788],[11,26747,26748],{},"OK this was so much fun: Google's Gemini 3 is amazing. I just got it to redesign my home page. I was having fun with this one so no real idea what I wanted just vibing along. It gave me a matrix style hero component which blew me away. This is so cool and the fact that I can spend less than an hour to improve my personal site is insane.",[11,26750,26751],{},"I used goose coding agent for this one which is open source and free and I just put my Gemini API key in which I am still using a free trial so my total cost for having fun was zero.",[11,26753,26754],{},"Was quite impressed that by giving Goose the link to an image it just downloaded it for me and added it to my public folder of my site. One less tedious task for me to do.",[11,26756,26757],{},"Towards the end I had the crazy idea of creating 7 hero component designs that change when you refresh the page. Why? Cause it's cool. This is maybe not how you build production apps but it sure is great for prototyping and getting to learn how new tools work and improving your communication with AI Agents and LLMs.",[11,26759,26760],{},"I encourage you all to take time out of your day and play around. Build a personal site even if you never deploy it. Improve your personal site and modify the design just for fun. Have fun cause Gemini 3 is pretty amazing and the tools we have available to us right now is insane.",[11,26762,26763],{},"And of course don't forget to run the Playwright healer agent after you have changed your design so your tests are updated. All it takes is a prompt. I didn't show it in this video but check out my other videos on Playwright Agents.",[11,26765,26766],{},"Have fun and happy vibe coding",[11,26768,26769],{},[58,26770,26771],{},"Links:",[70,26773,26774,26781],{},[73,26775,26776,26777],{},"Goose: AI Coding Agent: ",[15,26778,26779],{"href":26779,"rel":26780},"https://block.github.io/goose/",[19],[73,26782,26783,26784],{},"Gemini 3: ",[15,26785,26786],{"href":26786,"rel":26787},"https://blog.google/products/gemini/gemini-3/",[19],[26789,26790],"youtube",{"id":26791},"nSsBYokJefw",{"title":307,"searchDepth":748,"depth":748,"links":26793},[],"https://dev.to/debs_obrien/how-i-vibe-code-improving-my-site-design-with-goose-and-gemini-3-2a3k","2024-11-20","Having fun with Google's Gemini 3 and Goose coding agent to redesign my home page, creating dynamic hero components that change on refresh.",{},"/blog/how-i-vibe-code-improving-my-site-design-with-goose-and-gemini-3",{"title":26743,"description":26796},"blog/how-i-vibe-code-improving-my-site-design-with-goose-and-gemini-3",[795,796],"KvIoc6TjPq10bj-yPelxqTikQ-P9DZnrEmOO-GgpvOo",{"id":26804,"title":26805,"body":26806,"canonical":788,"date":26810,"description":26811,"extension":786,"featured":787,"image":26812,"meta":26813,"navigation":790,"ogimage":788,"path":26815,"provider":3460,"published":787,"seo":26816,"stem":26817,"tags":26818,"url":26819,"__hash__":26820},"blog/blog/how-learning-to-code-changed-my-life.md","How learning to code changed my life",{"type":8,"value":26807,"toc":26808},[],{"title":307,"searchDepth":748,"depth":748,"links":26809},[],"2018-05-14","A year ago, I was in a very different place. I had come to the end of a very long journey and I just didn’t know what to do next. I didn’t really have the interest or the strength to continue and I believed that I didn’t have a purpose anymore. I was in a deep, dark hole and I didn’t know how to get out of it.","v1607270030/debbie.codes/Bluekiri-office_gmihtu",{"platform":26814},"Treehouse","/blog/how-learning-to-code-changed-my-life",{"title":26805,"description":26811},"blog/how-learning-to-code-changed-my-life",[3464],"https://blog.teamtreehouse.com/learning-to-code-changed-my-life","dHt3jyXq3N3kVeE_20tWDQjkXe6aycS77mRa0DFJxjE",{"id":26822,"title":26823,"body":26824,"canonical":788,"date":27684,"description":27685,"extension":786,"featured":787,"image":27686,"meta":27687,"navigation":790,"ogimage":788,"path":27689,"provider":3460,"published":790,"seo":27690,"stem":27691,"tags":27692,"url":788,"__hash__":27693},"blog/blog/how-to-locate-elements-in-playwright.md","How to locate elements in Playwright",{"type":8,"value":26825,"toc":27667},[26826,26829,26840,26843,26847,26865,26870,26874,26889,26892,26941,26945,26953,26979,26992,27016,27020,27026,27054,27058,27061,27096,27100,27103,27136,27140,27147,27180,27189,27223,27229,27234,27236,27239,27248,27254,27265,27273,27286,27291,27295,27304,27310,27313,27316,27329,27335,27339,27342,27370,27376,27385,27413,27419,27423,27438,27486,27492,27496,27499,27502,27563,27570,27603,27606,27638,27644,27646,27649,27652,27664],[11,26827,26828],{},"In order to write end to end tests we need to first find elements on the webpage and then perform user actions on them. For example, find a link and click on it.",[1713,26830,26831],{},[11,26832,26833,26834,26839],{},"The most convenient way to find an element is to use ",[15,26835,26838],{"href":26836,"rel":26837},"https://playwright.dev/docs/codegen-intro",[19],"Playwright's test generator",", which allows you to not only generate tests but also generate a locator for a specific element.",[11,26841,26842],{},"But before we dive into how to use the test generator, first let's understand what a link element is and what exactly locators are.",[23,26844,26846],{"id":26845},"what-is-a-link","What is a link?",[11,26848,26849,26850,26857,26858,26860,26861,26864],{},"The HTML ",[15,26851,26854,7584],{"href":26852,"rel":26853},"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a",[19],[179,26855,26856],{},"\u003Ca>",", also known as the anchor element, is an interactive element that creates a hyperlink to another page either within your site or externally to another site. It can also be used to link to specific areas on the same page as well as for emails, file downloads or anything else a URL can address. The ",[179,26859,26856],{}," element uses the ",[179,26862,26863],{},"href"," attribute to pass in the URL that the hyperlink points to.",[1713,26866,26867],{},[11,26868,26869],{},"So how can we locate a link element so we can click it? We use Locators.",[23,26871,26873],{"id":26872},"what-are-locators","What are locators?",[11,26875,26876,26877,26882,26883,26888],{},"In Playwright we use a term called ",[15,26878,26881],{"href":26879,"rel":26880},"https://playwright.dev/docs/locators",[19],"Locators",". Locators represent a way to find elements on the page complete with ",[15,26884,26887],{"href":26885,"rel":26886},"https://playwright.dev/docs/actionability",[19],"auto waiting"," and retry-ability. Auto waiting means that Playwright performs a range of actionability checks on the elements, such as ensuring the element is visible and enabled before it performs the click.",[11,26890,26891],{},"You can locate elements by:",[70,26893,26894,26901,26908,26915,26921,26928,26934],{},[73,26895,26896],{},[15,26897,26900],{"href":26898,"rel":26899},"https://playwright.dev/docs/locators#locate-by-role",[19],"role",[73,26902,26903],{},[15,26904,26907],{"href":26905,"rel":26906},"https://playwright.dev/docs/locators#locate-by-label",[19],"label",[73,26909,26910],{},[15,26911,26914],{"href":26912,"rel":26913},"https://playwright.dev/docs/locators#locate-by-placeholder",[19],"placeholder",[73,26916,26917],{},[15,26918,304],{"href":26919,"rel":26920},"https://playwright.dev/docs/locators#locate-by-text",[19],[73,26922,26923],{},[15,26924,26927],{"href":26925,"rel":26926},"https://playwright.dev/docs/locators#locate-by-alt-text",[19],"alt text",[73,26929,26930],{},[15,26931,7176],{"href":26932,"rel":26933},"https://playwright.dev/docs/locators#locate-by-title",[19],[73,26935,26936],{},[15,26937,26940],{"href":26938,"rel":26939},"https://playwright.dev/docs/locators#locate-by-test-id",[19],"test id",[23,26942,26944],{"id":26943},"how-to-locate-a-link","How to locate a link",[11,26946,26849,26947,26952],{},[15,26948,26950,7584],{"href":26852,"rel":26949},[19],[179,26951,26856],{}," comes with the implicitly defined role of link, so we don't need to modify the HTML in order to get this role.",[299,26954,26956],{"className":21974,"code":26955,"language":21976,"meta":307,"style":307},"\u003Ca href=\"/about\">About\u003C/a>\n",[179,26957,26958],{"__ignoreMap":307},[1736,26959,26960,26962,26964,26967,26969,26972,26975,26977],{"class":1738,"line":1739},[1736,26961,6657],{"class":1912},[1736,26963,15],{"class":6696},[1736,26965,26966],{"class":2674}," href",[1736,26968,5062],{"class":1912},[1736,26970,26971],{"class":1935},"\"/about\"",[1736,26973,26974],{"class":1912},">About\u003C/",[1736,26976,15],{"class":6696},[1736,26978,6663],{"class":1912},[11,26980,26981,26982,26984,26985,26991],{},"That means we can locate an ",[179,26983,26856],{}," element by using the ",[15,26986,26988],{"href":26898,"rel":26987},[19],[179,26989,26990],{},"getByRole()"," locator, with the role of link and add the click method to the end of it.",[299,26993,26995],{"className":8734,"code":26994,"language":8736,"meta":307,"style":307},"await page.getByRole('link').click();\n",[179,26996,26997],{"__ignoreMap":307},[1736,26998,26999,27001,27003,27005,27007,27010,27012,27014],{"class":1738,"line":1739},[1736,27000,22662],{"class":4866},[1736,27002,22665],{"class":1912},[1736,27004,1032],{"class":2674},[1736,27006,7751],{"class":1912},[1736,27008,27009],{"class":1935},"'link'",[1736,27011,911],{"class":1912},[1736,27013,10804],{"class":2674},[1736,27015,15752],{"class":1912},[138,27017,27019],{"id":27018},"adding-a-name","Adding a name",[11,27021,27022,27023,27025],{},"This will work but of course what if you have more than one link on the page? That's where the ",[179,27024,15955],{}," option comes in. This is the accessible name for the link used by Assistive Technologies to identify the element. The name is normally taken from the elements content, an attribute, or from an associated element.",[299,27027,27029],{"className":8734,"code":27028,"language":8736,"meta":307,"style":307},"await page.getByRole('link', { name: 'About' }).click();\n",[179,27030,27031],{"__ignoreMap":307},[1736,27032,27033,27035,27037,27039,27041,27043,27045,27048,27050,27052],{"class":1738,"line":1739},[1736,27034,22662],{"class":4866},[1736,27036,22665],{"class":1912},[1736,27038,1032],{"class":2674},[1736,27040,7751],{"class":1912},[1736,27042,27009],{"class":1935},[1736,27044,10685],{"class":1912},[1736,27046,27047],{"class":1935},"'About'",[1736,27049,25393],{"class":1912},[1736,27051,10804],{"class":2674},[1736,27053,15752],{"class":1912},[138,27055,27057],{"id":27056},"using-regex","Using regex",[11,27059,27060],{},"We can also use a regular expression and ignore the case so that the test will pass regardless of if the text it contains has a capital letter or not.",[299,27062,27064],{"className":8734,"code":27063,"language":8736,"meta":307,"style":307},"await page.getByRole('link', { name: /about/i }).click();\n",[179,27065,27066],{"__ignoreMap":307},[1736,27067,27068,27070,27072,27074,27076,27078,27081,27083,27086,27088,27090,27092,27094],{"class":1738,"line":1739},[1736,27069,22662],{"class":4866},[1736,27071,22665],{"class":1912},[1736,27073,1032],{"class":2674},[1736,27075,7751],{"class":1912},[1736,27077,27009],{"class":1935},[1736,27079,27080],{"class":1912},", { name:",[1736,27082,13133],{"class":1935},[1736,27084,27085],{"class":13136},"about",[1736,27087,1066],{"class":1935},[1736,27089,13031],{"class":4866},[1736,27091,25393],{"class":1912},[1736,27093,10804],{"class":2674},[1736,27095,15752],{"class":1912},[138,27097,27099],{"id":27098},"setting-exact-to-true","Setting exact to true",[11,27101,27102],{},"You can set exact to true for an exact match. This is helpful if, for example, you have a link that contains the text \"About\" and another one that contains the text \"About the Company\". With exact set to true it will only match the link with \"About\" and not with \"About the Company\".",[299,27104,27106],{"className":8734,"code":27105,"language":8736,"meta":307,"style":307},"await page.getByRole('link', { name: 'About', exact: true }).click();\n",[179,27107,27108],{"__ignoreMap":307},[1736,27109,27110,27112,27114,27116,27118,27120,27122,27124,27127,27130,27132,27134],{"class":1738,"line":1739},[1736,27111,22662],{"class":4866},[1736,27113,22665],{"class":1912},[1736,27115,1032],{"class":2674},[1736,27117,7751],{"class":1912},[1736,27119,27009],{"class":1935},[1736,27121,10685],{"class":1912},[1736,27123,27047],{"class":1935},[1736,27125,27126],{"class":1912},", exact: ",[1736,27128,27129],{"class":1918},"true",[1736,27131,25393],{"class":1912},[1736,27133,10804],{"class":2674},[1736,27135,15752],{"class":1912},[138,27137,27139],{"id":27138},"adding-an-aria-label","Adding an aria-label",[11,27141,27142,27143,27146],{},"If a link contains an ",[179,27144,27145],{},"aria-label"," then the value of the aria label will be used instead of the text content.",[299,27148,27150],{"className":21974,"code":27149,"language":21976,"meta":307,"style":307},"\u003Ca href=\"/locators\" aria-label=\"read more about locators\">Read more\u003C/a>\n",[179,27151,27152],{"__ignoreMap":307},[1736,27153,27154,27156,27158,27160,27162,27165,27168,27170,27173,27176,27178],{"class":1738,"line":1739},[1736,27155,6657],{"class":1912},[1736,27157,15],{"class":6696},[1736,27159,26966],{"class":2674},[1736,27161,5062],{"class":1912},[1736,27163,27164],{"class":1935},"\"/locators\"",[1736,27166,27167],{"class":2674}," aria-label",[1736,27169,5062],{"class":1912},[1736,27171,27172],{"class":1935},"\"read more about locators\"",[1736,27174,27175],{"class":1912},">Read more\u003C/",[1736,27177,15],{"class":6696},[1736,27179,6663],{"class":1912},[11,27181,27182,27183,27188],{},"We can then locate the link using the ",[15,27184,27186],{"href":26898,"rel":27185},[19],[179,27187,26990],{}," locator of link followed by the value of the aria-label as the name.",[299,27190,27192],{"className":8734,"code":27191,"language":8736,"meta":307,"style":307},"await page.getByRole('link', { name: /read more about locators/i }).click();\n",[179,27193,27194],{"__ignoreMap":307},[1736,27195,27196,27198,27200,27202,27204,27206,27208,27210,27213,27215,27217,27219,27221],{"class":1738,"line":1739},[1736,27197,22662],{"class":4866},[1736,27199,22665],{"class":1912},[1736,27201,1032],{"class":2674},[1736,27203,7751],{"class":1912},[1736,27205,27009],{"class":1935},[1736,27207,27080],{"class":1912},[1736,27209,13133],{"class":1935},[1736,27211,27212],{"class":13136},"read more about locators",[1736,27214,1066],{"class":1935},[1736,27216,13031],{"class":4866},[1736,27218,25393],{"class":1912},[1736,27220,10804],{"class":2674},[1736,27222,15752],{"class":1912},[11,27224,27225,27226,27228],{},"When you are writing your tests it can be quite difficult to know which accessible role to use if using the ",[179,27227,26990],{}," locator. For a link it is pretty straight forward but there is a vast list of roles that can be used. If your element doesn't have an accessible locator then choosing the best locator to use is not easy especially if you are not familiar with the DOM structure of the site you are testing. So how can you choose the best locator?",[1713,27230,27231],{},[11,27232,27233],{},"Don't spend time trying to figure out which is the best locator to use. Instead use the test generator to generate your locator.",[731,27235],{},[23,27237,26838],{"id":27238},"playwrights-test-generator",[11,27240,27241,27242,27247],{},"You can generate code right from VS Code when using the ",[15,27243,27246],{"href":27244,"rel":27245},"https://playwright.dev/docs/getting-started-vscode#installation",[19],"VS Code extension"," by clicking the 'record new' link from the testing sidebar.",[11,27249,27250],{},[121,27251],{"alt":27252,"src":27253},"test generator in vs code editor","https://res.cloudinary.com/debsobrien/image/upload/v1669817488/debbie.codes/blog/2022/vs-code-codegen_gxf58x.png",[11,27255,27256,27257,27264],{},"As you click on an element on your website, the generator will look at your page and figure out the best locator for you, prioritizing role, text and test id locators. If the generator finds multiple elements matching the locator, it will improve the locator to make it resilient and uniquely identify the target element, so you don't have to worry about failing tests due to locators. For example, it could chain locators to narrow down the scope, or use the ",[15,27258,27261],{"href":27259,"rel":27260},"https://playwright.dev/docs/locators#filtering-locators",[19],[179,27262,27263],{},"filter()"," method to select between multiple elements.",[11,27266,27267,27268,27272],{},"If you are not using the VS Code extension, Playwright comes with a standalone test generator called [Codegen](",[15,27269,27270],{"href":27270,"rel":27271},"https://playwright.dev/docs/test",[19]," generator-intro).",[299,27274,27276],{"className":2665,"code":27275,"language":2667,"meta":307,"style":307},"npx playwright codegen\n",[179,27277,27278],{"__ignoreMap":307},[1736,27279,27280,27282,27284],{"class":1738,"line":1739},[1736,27281,2675],{"class":2674},[1736,27283,22593],{"class":1935},[1736,27285,25722],{"class":1935},[11,27287,27288],{},[121,27289],{"alt":22735,"src":27290},"https://res.cloudinary.com/debsobrien/image/upload/v1669647195/debbie.codes/blog/2022/codegen_qykizt.png",[138,27292,27294],{"id":27293},"pick-a-locator","Pick a locator",[11,27296,27297,27298,27303],{},"You can pick a locator by clicking the ",[15,27299,27302],{"href":27300,"rel":27301},"https://playwright.dev/docs/next/getting-started-vscode#picking-a-locator",[19],"pick locator"," button from the testing sidebar when using the VS Code Extension.",[11,27305,27306],{},[121,27307],{"alt":27308,"src":27309},"pick locator from vs code highlighting the locator in browser window","https://res.cloudinary.com/debsobrien/image/upload/v1669817100/debbie.codes/blog/2022/pick-locator-vscode_xrcdkc.png",[11,27311,27312],{},"When you hover over any element in the browser window the locator for that element will be highlighted under your cursor. If you click on the element the locator will appear in the 'pick locator' box. You can then copy it to your clipboard and paste it into your test.",[11,27314,27315],{},"You can also pick a locator when using the standalone test generator by clicking the explore button.",[299,27317,27319],{"className":2665,"code":27318,"language":2667,"meta":307,"style":307},"npx playwright open\n",[179,27320,27321],{"__ignoreMap":307},[1736,27322,27323,27325,27327],{"class":1738,"line":1739},[1736,27324,2675],{"class":2674},[1736,27326,22593],{"class":1935},[1736,27328,21187],{"class":1935},[11,27330,27331],{},[121,27332],{"alt":27333,"src":27334},"test generator generating a getByRole locator for a link","https://res.cloudinary.com/debsobrien/image/upload/v1669795104/debbie.codes/blog/2022/locator-link_hz7icx.png",[138,27336,27338],{"id":27337},"chaining-locators","Chaining locators",[11,27340,27341],{},"If your site contains duplicate links, for example two about links, one in the header and one in the footer, then the test generator will create a unique locator for each one. It does this by looking for an easily-identifiable ancestor of the link and chains the locators.",[299,27343,27345],{"className":8734,"code":27344,"language":8736,"meta":307,"style":307},"getByRole('navigation').getByRole('link', { name: 'About' })\n",[179,27346,27347],{"__ignoreMap":307},[1736,27348,27349,27351,27353,27356,27358,27360,27362,27364,27366,27368],{"class":1738,"line":1739},[1736,27350,1032],{"class":2674},[1736,27352,7751],{"class":1912},[1736,27354,27355],{"class":1935},"'navigation'",[1736,27357,911],{"class":1912},[1736,27359,1032],{"class":2674},[1736,27361,7751],{"class":1912},[1736,27363,27009],{"class":1935},[1736,27365,10685],{"class":1912},[1736,27367,27047],{"class":1935},[1736,27369,10691],{"class":1912},[11,27371,27372],{},[121,27373],{"alt":27374,"src":27375},"test generator highlighting the about link in the header","https://res.cloudinary.com/debsobrien/image/upload/v1669795104/debbie.codes/blog/2022/locator-chaining-header_hacas1.png",[11,27377,27378,27379,27384],{},"For the about link in the footer the test generator again looks for an ancestor and chains the two ",[15,27380,27382],{"href":26898,"rel":27381},[19],[179,27383,26990],{}," locators ensuring that there is only one element matching this locator.",[299,27386,27388],{"className":8734,"code":27387,"language":8736,"meta":307,"style":307},"getByRole('contentinfo').getByRole('link', { name: 'About' })\n",[179,27389,27390],{"__ignoreMap":307},[1736,27391,27392,27394,27396,27399,27401,27403,27405,27407,27409,27411],{"class":1738,"line":1739},[1736,27393,1032],{"class":2674},[1736,27395,7751],{"class":1912},[1736,27397,27398],{"class":1935},"'contentinfo'",[1736,27400,911],{"class":1912},[1736,27402,1032],{"class":2674},[1736,27404,7751],{"class":1912},[1736,27406,27009],{"class":1935},[1736,27408,10685],{"class":1912},[1736,27410,27047],{"class":1935},[1736,27412,10691],{"class":1912},[11,27414,27415],{},[121,27416],{"alt":27417,"src":27418},"test generator highlighting the footer link","https://res.cloudinary.com/debsobrien/image/upload/v1669795104/debbie.codes/blog/2022/locator-chaining-footer_ogczv7.png",[138,27420,27422],{"id":27421},"filtering-locators","Filtering locators",[11,27424,27425,27426,27431,27432,27437],{},"If the test generator can't give you a unique locator through chaining it will use the ",[15,27427,27429],{"href":27259,"rel":27428},[19],[179,27430,27263],{}," method to ensure the locator is unique. For example, we might have two lists that contain topics for blog posts. The first list is used to filter the blog posts and the second list is used to display the topics for each post. The test generator will use the ",[15,27433,27435],{"href":27259,"rel":27434},[19],[179,27436,27263],{}," method to ensure the locator is unique.",[299,27439,27441],{"className":8734,"code":27440,"language":8736,"meta":307,"style":307},"getByRole('list')\n  .filter({ hasText: 'architecturedev reljamstackjavascriptpersonalmentoringmotivationnuxtperformance' })\n  .getByRole('link', { name: 'architecture' })\n",[179,27442,27443,27454,27469],{"__ignoreMap":307},[1736,27444,27445,27447,27449,27452],{"class":1738,"line":1739},[1736,27446,1032],{"class":2674},[1736,27448,7751],{"class":1912},[1736,27450,27451],{"class":1935},"'list'",[1736,27453,7045],{"class":1912},[1736,27455,27456,27459,27461,27464,27467],{"class":1738,"line":748},[1736,27457,27458],{"class":1912},"  .",[1736,27460,14507],{"class":2674},[1736,27462,27463],{"class":1912},"({ hasText: ",[1736,27465,27466],{"class":1935},"'architecturedev reljamstackjavascriptpersonalmentoringmotivationnuxtperformance'",[1736,27468,10691],{"class":1912},[1736,27470,27471,27473,27475,27477,27479,27481,27484],{"class":1738,"line":756},[1736,27472,27458],{"class":1912},[1736,27474,1032],{"class":2674},[1736,27476,7751],{"class":1912},[1736,27478,27009],{"class":1935},[1736,27480,10685],{"class":1912},[1736,27482,27483],{"class":1935},"'architecture'",[1736,27485,10691],{"class":1912},[11,27487,27488],{},[121,27489],{"alt":27490,"src":27491},"test generator generating a list locator with filter method","https://res.cloudinary.com/debsobrien/image/upload/v1669795104/debbie.codes/blog/2022/locator-filtering_hvlbco.png",[138,27493,27495],{"id":27494},"improving-your-locators","Improving your locators",[11,27497,27498],{},"You may notice that filtering by text can sometimes give you a really ugly locator, like in the example above, and may cause you issues later, especially if you add another topic to the filters.",[11,27500,27501],{},"You can improve this filter by using a regular expression to match certain words in the text, choosing ones that would normally not be seen together in a post such as 'architecture', 'mentoring' and 'testing'.",[299,27503,27505],{"className":8734,"code":27504,"language":8736,"meta":307,"style":307},"getByRole('list')\n  .filter({ hasText: /architecture.*mentoring.*testing/ })\n  .getByRole('link', { name: 'architecture' })\n",[179,27506,27507,27517,27547],{"__ignoreMap":307},[1736,27508,27509,27511,27513,27515],{"class":1738,"line":1739},[1736,27510,1032],{"class":2674},[1736,27512,7751],{"class":1912},[1736,27514,27451],{"class":1935},[1736,27516,7045],{"class":1912},[1736,27518,27519,27521,27523,27526,27528,27531,27533,27535,27537,27539,27541,27543,27545],{"class":1738,"line":748},[1736,27520,27458],{"class":1912},[1736,27522,14507],{"class":2674},[1736,27524,27525],{"class":1912},"({ hasText:",[1736,27527,13133],{"class":1935},[1736,27529,27530],{"class":13136},"architecture",[1736,27532,891],{"class":1918},[1736,27534,14968],{"class":4866},[1736,27536,20921],{"class":13136},[1736,27538,891],{"class":1918},[1736,27540,14968],{"class":4866},[1736,27542,1411],{"class":13136},[1736,27544,1066],{"class":1935},[1736,27546,10691],{"class":1912},[1736,27548,27549,27551,27553,27555,27557,27559,27561],{"class":1738,"line":756},[1736,27550,27458],{"class":1912},[1736,27552,1032],{"class":2674},[1736,27554,7751],{"class":1912},[1736,27556,27009],{"class":1935},[1736,27558,10685],{"class":1912},[1736,27560,27483],{"class":1935},[1736,27562,10691],{"class":1912},[11,27564,27565,27566,27569],{},"Another option is to add an aria-label to the first ",[179,27567,27568],{},"\u003Cul>"," element with the value of topics. This not only helps improve the accessibility of the page but also allows Playwright to locate by role.",[299,27571,27573],{"className":21974,"code":27572,"language":21976,"meta":307,"style":307},"\u003Cul aria-label=\"topics\">\n  //...\n\u003C/ul>\n",[179,27574,27575,27590,27595],{"__ignoreMap":307},[1736,27576,27577,27579,27581,27583,27585,27588],{"class":1738,"line":1739},[1736,27578,6657],{"class":1912},[1736,27580,70],{"class":6696},[1736,27582,27167],{"class":2674},[1736,27584,5062],{"class":1912},[1736,27586,27587],{"class":1935},"\"topics\"",[1736,27589,6663],{"class":1912},[1736,27591,27592],{"class":1738,"line":748},[1736,27593,27594],{"class":1912},"  //...\n",[1736,27596,27597,27599,27601],{"class":1738,"line":756},[1736,27598,8105],{"class":1912},[1736,27600,70],{"class":6696},[1736,27602,6663],{"class":1912},[11,27604,27605],{},"The test generator will now use this aria-label and locate by the role of list with the name of topics therefore generating a unique locator.",[299,27607,27609],{"className":8734,"code":27608,"language":8736,"meta":307,"style":307},"getByRole('list', { name: 'topics' }).getByRole('link', { name: 'architecture' })\n",[179,27610,27611],{"__ignoreMap":307},[1736,27612,27613,27615,27617,27619,27621,27624,27626,27628,27630,27632,27634,27636],{"class":1738,"line":1739},[1736,27614,1032],{"class":2674},[1736,27616,7751],{"class":1912},[1736,27618,27451],{"class":1935},[1736,27620,10685],{"class":1912},[1736,27622,27623],{"class":1935},"'topics'",[1736,27625,25393],{"class":1912},[1736,27627,1032],{"class":2674},[1736,27629,7751],{"class":1912},[1736,27631,27009],{"class":1935},[1736,27633,10685],{"class":1912},[1736,27635,27483],{"class":1935},[1736,27637,10691],{"class":1912},[11,27639,27640],{},[121,27641],{"alt":27642,"src":27643},"test generator generating a list locator with aria-label","https://res.cloudinary.com/debsobrien/image/upload/v1669795104/debbie.codes/blog/2022/locator-filtering-aria-label_xkdrep.png",[23,27645,3294],{"id":3293},[11,27647,27648],{},"There are many other roles and locators that we didn't touch on in this post. The great thing is that you don't have to know which ones are available to you because thanks to the test generator you don't have to worry about which role or locator to choose when writing your tests. This makes for a much better developer experience and ensures you have resilient locators whether you are new to testing or not.",[11,27650,27651],{},"And of course if the test generator generates a locator that you are not happy with then it might be worth investigating the DOM to see if you can improve your code to be more accessible, and in that way the test generator can generate more accessible locators. This gives you better tests and improved code at the same time.",[11,27653,27654,27655,27659,27660,27663],{},"I encourage you to give the test generator a try either using the ",[15,27656,27246],{"href":27657,"rel":27658},"https://playwright.dev/docs/getting-started-vscode",[19]," or opening it from the terminal with ",[179,27661,27662],{},"npx playwright codegen",". Happy testing.",[2011,27665,27666],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":27668},[27669,27670,27671,27677,27683],{"id":26845,"depth":748,"text":26846},{"id":26872,"depth":748,"text":26873},{"id":26943,"depth":748,"text":26944,"children":27672},[27673,27674,27675,27676],{"id":27018,"depth":756,"text":27019},{"id":27056,"depth":756,"text":27057},{"id":27098,"depth":756,"text":27099},{"id":27138,"depth":756,"text":27139},{"id":27238,"depth":748,"text":26838,"children":27678},[27679,27680,27681,27682],{"id":27293,"depth":756,"text":27294},{"id":27337,"depth":756,"text":27338},{"id":27421,"depth":756,"text":27422},{"id":27494,"depth":756,"text":27495},{"id":3293,"depth":748,"text":3294},"2022-11-30","How can we locate a link click on it in our e2e tests. In this article we will learn how to locate elements in Playwright using the test generator to generate your tets based on user actions or pick a locator by clicking on an element","v1669817488/debbie.codes/blog/2022/vs-code-codegen_gxf58x.png",{"alt":27688,"ogImage":27253},"browser window showing highlighted element and inspector window with explore showing the locator highlighted","/blog/how-to-locate-elements-in-playwright",{"title":26823,"description":27685},"blog/how-to-locate-elements-in-playwright",[1411,1412],"FeD9mkCtBOZMGydbFDS33oUq83iZgotylPbQw6eSjEs",{"id":27695,"title":27696,"body":27697,"canonical":788,"date":27701,"description":27702,"extension":786,"featured":787,"image":27703,"meta":27704,"navigation":790,"ogimage":788,"path":27706,"provider":3460,"published":787,"seo":27707,"stem":27708,"tags":27709,"url":27710,"__hash__":27711},"blog/blog/how-we-use-nuxt-at-the-nuxtjs-company.md","How we use Nuxt at The NuxtJS Company",{"type":8,"value":27698,"toc":27699},[],{"title":307,"searchDepth":748,"depth":748,"links":27700},[],"2020-10-22","The first commit for Nuxt.js on Github was made on October 26th, 2016. Since then the Nuxt.js framework has grown from 2 creators in 2016 to having 10 full-time employees at the NuxtJS company, as well as the amazing Nuxt community with maintainers, ambassadors and contributors.","v1630862648/debbie.codes/featured-posts/how-we-use-nuxt_zx05uz",{"loading":3458,"platform":27705},"Microsoft","/blog/how-we-use-nuxt-at-the-nuxtjs-company",{"title":27696,"description":27702},"blog/how-we-use-nuxt-at-the-nuxtjs-company",[5239],"https://devblogs.microsoft.com/startups/nuxt/?WT.mc_id=startups-7534-cxa","v7T5zrZfE94LUuOLVqi7Mm1_Ao5y2g5spnA9coaYpCQ",{"id":27713,"title":27714,"body":27715,"canonical":27925,"date":27926,"description":27719,"extension":786,"featured":787,"image":788,"meta":27927,"navigation":790,"ogimage":788,"path":27928,"provider":788,"published":790,"seo":27929,"stem":27930,"tags":27931,"url":788,"__hash__":27932},"blog/blog/i-built-my-own-ai-agent-and-you-can-too.md","I Built My Own AI Agent using n8n — And You Can Too",{"type":8,"value":27716,"toc":27910},[27717,27720,27723,27727,27730,27744,27747,27751,27754,27757,27768,27771,27775,27778,27782,27791,27795,27802,27806,27809,27813,27816,27824,27827,27833,27837,27840,27844,27847,27853,27857,27860,27863,27866,27872,27875,27879,27882,27896,27899,27901,27904,27907],[11,27718,27719],{},"I recently built my own AI agent. Not because I needed one, but because I wanted to see how far I could take the current tools available. It turned out to be surprisingly straightforward, extremely flexible, and genuinely useful.",[11,27721,27722],{},"This post will show you what the agent can do and how you can build your own using n8n, without needing to write much code.",[23,27724,27726],{"id":27725},"what-my-agent-can-do","What My Agent Can Do",[11,27728,27729],{},"The agent runs as a simple chat interface where I can ask it different questions and request actions. For example:",[2260,27731,27732,27735,27738,27741],{},[73,27733,27734],{},"Ask for the weather in a city.",[73,27736,27737],{},"Get my current YouTube subscriber count.",[73,27739,27740],{},"Check my recent running stats from Strava.",[73,27742,27743],{},"Have it send me an email containing a summary of the full conversation.",[11,27745,27746],{},"This is not necessarily something you must have, but it is a great demonstration of what is possible when you combine multiple data sources and let an AI model reason across them.",[23,27748,27750],{"id":27749},"the-tool-i-used-n8n","The Tool I Used: n8n",[11,27752,27753],{},"I built everything using n8n, a visual workflow automation tool. It allows you to drag and drop nodes to create automation steps, including AI reasoning steps.",[11,27755,27756],{},"One workflow contains:",[2260,27758,27759,27762,27765],{},[73,27760,27761],{},"A chat trigger",[73,27763,27764],{},"A large language model (Gemini in my case, but OpenAI also works)",[73,27766,27767],{},"Tools such as:\n• YouTube API statistics\n• Strava activity data\n• Weather API queries\n• GitHub issues\n• Gmail actions (send summaries, notifications, etc)",[11,27769,27770],{},"One important component is conversation memory, which allows the agent to recall previous messages in the chat.",[23,27772,27774],{"id":27773},"step-by-step-quick-start-guide","Step-by-Step Quick Start Guide",[11,27776,27777],{},"Follow these steps to create your first simple agent.",[138,27779,27781],{"id":27780},"step-1-create-an-n8n-account","Step 1: Create an n8n Account",[11,27783,27784,27785,27790],{},"Go to ",[15,27786,27789],{"href":27787,"rel":27788},"https://n8n.io/",[19],"https://n8n.io"," and create a free account. You do not need to enter payment details to start.",[138,27792,27794],{"id":27793},"step-2-create-a-new-workflow","Step 2: Create a New Workflow",[11,27796,27797,27798],{},"Inside n8n, click Workflows and choose New Workflow or use a template for free like the one I used: ",[15,27799,27800],{"href":27800,"rel":27801},"https://n8n.io/workflows/6270-build-your-first-ai-agent/",[19],[138,27803,27805],{"id":27804},"step-3-add-a-chat-trigger","Step 3: Add a Chat Trigger",[11,27807,27808],{},"Search for AI Chat Trigger and add it to your workflow. This will provide the public chat interface URL.",[138,27810,27812],{"id":27811},"step-4-add-an-ai-model-node","Step 4: Add an AI Model Node",[11,27814,27815],{},"Search for Google Gemini or OpenAI Chat Model.",[11,27817,27818,27819,27823],{},"You will need:\n• A Google Cloud key (if using Gemini) ",[15,27820,27821],{"href":27821,"rel":27822},"https://console.cloud.google.com/",[19],"\n• Or an OpenAI key (if using GPT models)",[11,27825,27826],{},"Set a system prompt, such as:",[299,27828,27831],{"className":27829,"code":27830,"language":304},[302],"You are a personal assistant that can answer questions and use tools when needed.\n",[179,27832,27830],{"__ignoreMap":307},[138,27834,27836],{"id":27835},"step-5-add-conversation-memory","Step 5: Add Conversation Memory",[11,27838,27839],{},"Search for Memory and connect it to your agent. This allows your agent to remember context across multiple messages.",[138,27841,27843],{"id":27842},"step-6-add-your-first-tool-example-weather-api","Step 6: Add Your First Tool (Example: Weather API)",[11,27845,27846],{},"Search for HTTP Request. Set a request such as:",[299,27848,27851],{"className":27849,"code":27850,"language":304},[302],"GET https://api.open-meteo.com/v1/forecast\n",[179,27852,27850],{"__ignoreMap":307},[138,27854,27856],{"id":27855},"step-7-save-and-activate-the-workflow","Step 7: Save and Activate the Workflow",[11,27858,27859],{},"Click Save and then toggle the workflow to Active.",[11,27861,27862],{},"Copy the chat URL and open it in your browser.",[11,27864,27865],{},"Ask your agent something like:",[299,27867,27870],{"className":27868,"code":27869,"language":304},[302],"What is the weather right now in Palma?\n",[179,27871,27869],{"__ignoreMap":307},[11,27873,27874],{},"You just built your first agent.",[23,27876,27878],{"id":27877},"expanding-with-more-tools","Expanding with More Tools",[11,27880,27881],{},"Once the foundation is set, add tools one at a time. Examples from my workflow:",[2260,27883,27884,27887,27890,27893],{},[73,27885,27886],{},"YouTube Data API to fetch subscriber counts",[73,27888,27889],{},"Strava API to retrieve recent runs",[73,27891,27892],{},"GitHub API to check open issues",[73,27894,27895],{},"Gmail node to send email summaries",[11,27897,27898],{},"Each tool is added as a separate node. Connect it as a tool to the AI model. Test each step individually before chaining them.",[23,27900,25046],{"id":25045},[11,27902,27903],{},"Building an AI agent today does not require advanced development skills. The hardest part is usually obtaining and configuring API credentials. Once those are set, everything becomes easier.",[11,27905,27906],{},"Experiment, try small ideas, and let your agent grow naturally as new needs appear.",[11,27908,27909],{},"If you build one, I would be interested in hearing how it went and what use cases you explored.",{"title":307,"searchDepth":748,"depth":748,"links":27911},[27912,27913,27914,27923,27924],{"id":27725,"depth":748,"text":27726},{"id":27749,"depth":748,"text":27750},{"id":27773,"depth":748,"text":27774,"children":27915},[27916,27917,27918,27919,27920,27921,27922],{"id":27780,"depth":756,"text":27781},{"id":27793,"depth":756,"text":27794},{"id":27804,"depth":756,"text":27805},{"id":27811,"depth":756,"text":27812},{"id":27835,"depth":756,"text":27836},{"id":27842,"depth":756,"text":27843},{"id":27855,"depth":756,"text":27856},{"id":27877,"depth":748,"text":27878},{"id":25045,"depth":748,"text":25046},"https://dev.to/debs_obrien/i-built-my-own-ai-agent-and-you-can-too-56l1","2024-11-06",{},"/blog/i-built-my-own-ai-agent-and-you-can-too",{"title":27714,"description":27719},"blog/i-built-my-own-ai-agent-and-you-can-too",[795],"eu7Zrv7twv5-wRMteZQDd3Hcbi5W1aHt9wUeEJe_CaA",{"id":27934,"title":27935,"body":27936,"canonical":28006,"date":28015,"description":28016,"extension":786,"featured":787,"image":788,"meta":28017,"navigation":790,"ogimage":788,"path":28018,"provider":788,"published":787,"seo":28019,"stem":28020,"tags":28021,"url":788,"__hash__":28022},"blog/blog/install-playwright-mcp-server-in-vs-code.md","Install Playwright MCP Server in VS Code",{"type":8,"value":27937,"toc":28008},[27938,27941,27943,27954,27958,27961,27965,27968,27971,27975,27978,27981,27984,27991,27995,27999],[11,27939,27940],{},"Installing MCP(Model Context Protocol) servers in Visual Studio Code just got a major upgrade! With the latest update, you'll notice a new MCP Servers section in the Extensions panel. Here you will find all the MCP servers you already have installed. You should also see a \"world\" icon for browsing MCP Servers.",[23,27942,15435],{"id":15434},[70,27944,27945,27948,27951],{},[73,27946,27947],{},"Open VS Code and click on the Extensions panel",[73,27949,27950],{},"Under extensions is a new section called MCP Servers Installed",[73,27952,27953],{},"Look for the new globe/world icon and click it to launch the MCP server browser",[23,27955,27957],{"id":27956},"browse-available-mcp-servers","Browse Available MCP Servers",[11,27959,27960],{},"You'll be greeted by a curated list of available MCP servers, each with a description and quick actions.",[23,27962,27964],{"id":27963},"install-an-mcp-server","Install an MCP Server",[11,27966,27967],{},"Find the server you want and click the Install button.\nThis will open a web link that triggers VS Code to handle the installation for you.",[11,27969,27970],{},"Confirm the install in VS Code—no need to copy-paste commands or hunt for repositories.",[23,27972,27974],{"id":27973},"manage-your-servers","Manage Your Servers",[11,27976,27977],{},"Once installed, your MCP servers appear in the extension's sidebar.\nYou can start or stop servers with a single click.",[11,27979,27980],{},"Click on a server to view its README, check documentation, or access configuration options directly from the UI.",[11,27982,27983],{},"Give it a try and see how much easier MCP server installation can be!",[11,27985,27986,27987],{},"Learn more at: ",[15,27988,27989],{"href":27989,"rel":27990},"https://code.visualstudio.com/mcp",[19],[23,27992,27994],{"id":27993},"watch-the-tutorial","Watch the Tutorial",[24579,27996],{"videoid":27997,"playlabel":27998},"exsikHe20D8","Install an MCP Server in VS Code. (Watch me install Playwright MCP)",[11,28000,28001],{},[133,28002,28003,28004],{},"Originally published on ",[15,28005,5508],{"href":28006,"rel":28007},"https://dev.to/debs_obrien/install-playwright-mcp-server-in-vs-code-4o91",[19],{"title":307,"searchDepth":748,"depth":748,"links":28009},[28010,28011,28012,28013,28014],{"id":15434,"depth":748,"text":15435},{"id":27956,"depth":748,"text":27957},{"id":27963,"depth":748,"text":27964},{"id":27973,"depth":748,"text":27974},{"id":27993,"depth":748,"text":27994},"2024-07-15","Installing MCP(Model Context Protocol) servers in Visual Studio Code just got a major upgrade! Learn how to use the new MCP Servers section in the Extensions panel to easily browse, install, and manage MCP servers.",{},"/blog/install-playwright-mcp-server-in-vs-code",{"title":27935,"description":28016},"blog/install-playwright-mcp-server-in-vs-code",[1412,3321,795,22558],"4iE-awT6JG0Z_1y9V4HW3rWc3X5QFxnRCAOXp70ksmo",{"id":28024,"title":28025,"body":28026,"canonical":788,"date":28456,"description":28457,"extension":786,"featured":787,"image":28458,"meta":28459,"navigation":790,"ogimage":788,"path":28461,"provider":3460,"published":790,"seo":28462,"stem":28463,"tags":28464,"url":788,"__hash__":28465},"blog/blog/interviewing-with-the-big-tech-companies.md","Interviewing with the Big Tech Companies",{"type":8,"value":28027,"toc":28432},[28028,28032,28035,28038,28042,28045,28049,28052,28058,28061,28065,28068,28077,28083,28086,28090,28104,28113,28119,28122,28142,28145,28149,28174,28178,28192,28200,28204,28207,28210,28213,28216,28220,28223,28226,28230,28233,28236,28239,28243,28246,28255,28261,28264,28267,28271,28274,28277,28281,28284,28290,28293,28297,28300,28303,28306,28310,28313,28316,28319,28323,28326,28329,28332,28335,28339,28342,28345,28354,28357,28361,28364,28367,28371,28374,28377,28381,28384,28388,28391,28395,28398,28401,28405,28408,28411,28417,28420,28423],[23,28029,28031],{"id":28030},"it-all-started-with-meta","It all Started with Meta",[11,28033,28034],{},"It actually all started when I got a message from a Meta recruiter who insisted I have a chat with him. So I said, ok a chat, but I am not looking for a job. However when I went home at Christmas and told my family that a recruiter from Meta, from America, had called me, well it just opened my eyes to the possibility that maybe just maybe I was actually good enough to work for the big tech companies. Maybe it was time to make my dream come true. My family of course all told me that I could do it. They always think I can achieve anything.",[11,28036,28037],{},"So I decided to give it a go. Meta didn't work out for reasons I do not know, something about US roles and Europe but ok, no problem.",[23,28039,28041],{"id":28040},"then-came-google","Then Came Google",[11,28043,28044],{},"On the 24th of January, on LinkedIn I saw a role for a developer advocate position at Google and I thought, why not. I mean, there was no way I was going to get it, but it would be a good experience to see what the process was like.",[138,28046,28048],{"id":28047},"google-recruiter","Google Recruiter",[11,28050,28051],{},"On the 25th January I got an email from Google recruiter asking me when I was available for a call to have a chat about my work history and experience. On the 26th January I had the call. The call went really well and shortly after it they sent me an email asking me to give them some dates that I would be available between the 7th and 18th of February. The email contained a very long list of how to prepare for the interview and what I should study, read, watch etc.",[11,28053,28054],{},[121,28055],{"alt":28056,"src":28057},"email of list of things to stud","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651654531/debbie.codes/blog/2022/email-google-coding_2x_tq5mnm.png",[11,28059,28060],{},"I sent them a few dates and they chose the 14th of February, Valentines day. I now had just over 2 weeks to learn everything about data structures, algorithms, and all sorts of things I had no idea about. People spend 6 months preparing for an interview at Google and I began to see why.",[138,28062,28064],{"id":28063},"planning-for-the-interview","Planning for the Interview",[11,28066,28067],{},"So I created a plan. First I took everything from the email and added it to a notion doc separated by the categories, reading, coding challenges, videos and courses. Once column had the link and the other had a comment where I would write done or read again or great. I now had a long list of things on Notion that I needed to watch, read and study.",[11,28069,28070,28071,28076],{},"I started by watching some of the ",[15,28072,28075],{"href":28073,"rel":28074},"https://www.youtube.com/watch?v=XKu_SEDAykw",[19],"videos from Google on interviewing there"," and the whiteboard interview. I realised I was way over my head and there was no way I could just read the material that was sent to me and be able to do the interview. I tried some of the leet code challenges but I really had no idea how to tackle them. This is when you begin to wish you had studied a computer science degree.",[11,28078,28079],{},[121,28080],{"alt":28081,"src":28082},"a man being interviewed by a women and using the whiteboard to write the solution to the coding challenge","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651677846/debbie.codes/blog/2022/google-interview_2x_jzrje9.png",[11,28084,28085],{},"I needed a new plan. Something that was going to get me up to speed fast. I needed to understand how things worked before I could start doing challenges. I was missing a lot of the basics around data structures and algorithms.",[138,28087,28089],{"id":28088},"courses-on-computer-science","Courses on Computer Science",[11,28091,28092,28093,28098,28099,891],{},"I started by doing the ",[15,28094,28097],{"href":28095,"rel":28096},"https://frontendmasters.com/courses/computer-science-v2/",[19],"Complete Intro to Computer Science course on Frontend Master"," by Brian Holt. This has actually been on my todo list for quite a while so now was the time to do it. This helped me a lot but also scared me even more. There are a lot of things to know about. I next studied Bianca Gandolofo's course on ",[15,28100,28103],{"href":28101,"rel":28102},"https://frontendmasters.com/courses/data-structures-interviews/",[19],"an Introduction to Data Structures for Interviews",[11,28105,28106,28107,28112],{},"At the same time I also enrolled in ",[15,28108,28111],{"href":28109,"rel":28110},"https://www.edx.org/course/introduction-computer-science-harvardx-cs50x",[19],"Harvard's online CS50's Introduction to Computer Science",". Although I found everything a little overwhelming I was also really enjoying learning all this and understanding how sorts work under the hood and when to choose one over the other.",[11,28114,28115],{},[121,28116],{"alt":28117,"src":28118},"David J. Malan on stage at Harvard teaching Linear Search using lockers and numbers","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651677615/debbie.codes/blog/2022/linear-search-harvard_2x_ejhpgf.png",[11,28120,28121],{},"I also think this is one of the most amazing courses I have ever taken and am so glad I signed up for it. David J. Malan, the instructor, has a way of explaining the concepts in such a way that you fully understand why and even remember it.",[11,28123,28124,28125,608,28130,28135,28136,28141],{},"I then did Bianca's other courses on Frontend Masters, ",[15,28126,28129],{"href":28127,"rel":28128},"https://frontendmasters.com/courses/practical-algorithms/",[19],"A Practical Guide to Algorithms with JavaScript",[15,28131,28134],{"href":28132,"rel":28133},"https://frontendmasters.com/courses/trees-and-graphs/",[19],"Tree and Graph Data Structures"," as well as ",[15,28137,28140],{"href":28138,"rel":28139},"https://frontendmasters.com/courses/interviewing-frontend/",[19],"Interviewing for Front-End Engineers"," by Jem Young.",[11,28143,28144],{},"You might ask, how on earth did you retain all that information and the truth is I didn't. I was doing a crash course in computer science in the space of two weeks and although I was taking many many notes and really understanding things there was no way I was retaining all that information.",[138,28146,28148],{"id":28147},"reading-about-big-0-and-so-much-more","Reading about Big 0 and so much more",[11,28150,28151,28152,28157,28158,829,28163,28135,28168,28173],{},"I was now getting close to my interview date and so I started reading all the material that was sent to me from ",[15,28153,28156],{"href":28154,"rel":28155},"https://www.interviewcake.com/article/java/big-o-notation-time-and-space-complexity",[19],"Big O Notation"," to books such as ",[15,28159,28162],{"href":28160,"rel":28161},"https://technicalinterviews.dev/",[19],"De-Coding the Technical Interview Process",[15,28164,28167],{"href":28165,"rel":28166},"https://www.amazon.com/Cracking-Coding-Interview-Programming-Questions/dp/0984782850",[19],"Cracking the Coding Interview",[15,28169,28172],{"href":28170,"rel":28171},"https://www.amazon.com/Think-Like-Programmer-Introduction-Creative/dp/1593274246",[19],"Think like a programmer",". Of course I didn't read all of these in depth but read though as much of them as I could taking notes along the way.",[138,28175,28177],{"id":28176},"attempting-the-coding-challenges","Attempting the Coding Challenges",[11,28179,28180,28181,28135,28186,28191],{},"I then went back to trying out some ",[15,28182,28185],{"href":28183,"rel":28184},"https://leetcode.com/",[19],"Leet code challenges",[15,28187,28190],{"href":28188,"rel":28189},"https://www.topcoder.com/",[19],"Top Coder"," but really I was no where near ready enough for these challenges although I did find them easier to understand after all the prep I had done and I managed to finish some of the easy ones but it took me hours instead of minutes.",[11,28193,28194,28195,28199],{},"There was no way I was going to pass a ",[15,28196,28198],{"href":28073,"rel":28197},[19],"Google interview"," even though it looked like a lot of fun. I did a mock interview with a friend and although it helped it just showed me that I was not prepared at all. I needed more time and I didn't have any more time.",[23,28201,28203],{"id":28202},"the-white-board-interview","The White Board Interview",[11,28205,28206],{},"So on the 14th February I had my interview. The guy was really nice and we had a great chat and he asked me lots of questions which I felt I answered very well. Then came the coding part. I would describe it like coding on a google docs. There was no syntax highlighting and it was far from a code editor.",[11,28208,28209],{},"The challenge was not as difficult as I thought it would be. Actually I didn't get asked to do anything around bubble sort or merge sort or anything like that at all. The challenge was much more frontend focused.",[11,28211,28212],{},"However I had just spent the last few weeks learning data structures and doing coding challenges in C+ and now my brain was just not doing what it should be doing and so I just forget everything I know. I couldn't remember simple things that I normally teach. I couldn't use the docs or Google or GitHub copilot or anything that I normally use to code.",[11,28214,28215],{},"I forget how to write certain syntax and then I just couldn't move forward or see a solution. I talked though the whole interview process and explained other things but I was not actually doing the challenge that had been asked of me. I was going round in circles and making it worse and although the interviewer tried to help me there was just no hope. My brain was just not able to do any more.",[138,28217,28219],{"id":28218},"feeling-like-a-failure","Feeling like a Failure",[11,28221,28222],{},"I felt like a complete idiot. I can do so much better than that. I have no idea why I messed up so badly. I have never ever done a white board interview before, not in real life or virtually and let me tell you they are hard cause they just take you away from your normal comfortable environment. They are also not a real scenario. It is not how we normally code and get things done. I rely on docs and Googling because I don't always remember how to do things and I certainly don't know all the syntax off by heart. I actually use code snippets, some of which I created myself, in order to write certain code.",[11,28224,28225],{},"So yes I basically felt like a useless programer after that interview and terribly annoyed with myself. It wasn't about getting the job as there was a whole further process after that and I wasn't sure if I would make it further or not. I just wanted to have shown them that I am actually a much better programmer than what they seen in those 30 minutes. But that's what you get to prove yourself and I didn't do a good job at all.",[138,28227,28229],{"id":28228},"obviously-it-was-a-no","Obviously it was a NO",[11,28231,28232],{},"When I got the call from the recruiter a few days later, he told me they had expected me to do better at the coding challenge and I said, yes, I agree. I expected to do better myself, believe me. He told m they would like to keep in contact with me and reach out again in 6 months time to try again. This was nice as it wasn't a no forever just a you need more time to learn this and be ready to pass this interview process.",[11,28234,28235],{},"I also gave him some feedback on material sent to me to study versus what was asked during the interview. Had I not studied and done Leet code challenges in C+ would my brain have been able to manage the JavaScript challenge more easily? Who knows. I don't regret studying everything I studied but if I had of live streamed frontend stuff for the last few weeks instead of studying everything I did I would have done a better job in the interview.",[11,28237,28238],{},"But I went out that night and had a Margarita and celebrated the fact that I had gotten this far and that that itself was a major achievement and something to be proud of even if I didn't make it to the next round.",[23,28240,28242],{"id":28241},"and-then-there-was-microsoft","And Then There was Microsoft",[11,28244,28245],{},"On the 4th of February a friend told me about a position at Microsoft and encouraged me to apply. I was referred for the position but to be honest I didn't really have high hopes because I was still not ready to pass these white board interviews at all and I was still struggling to learn data structures and algorithms.",[11,28247,28248,28249,28254],{},"Also I didn't feel I was good enough for the role or had enough knowledge or experience and even asked my fellow Microsoft friends if they thought I was good enough to work for Microsoft. Yes ",[15,28250,28253],{"href":28251,"rel":28252},"https://debbie.codes/blog/being-an-imposter",[19],"imposter-syndrome",", is a nightmare.",[11,28256,28257],{},[121,28258],{"alt":28259,"src":28260},"tweets messages about me doubting im good enough to join Microsoft and being told I have everything I need","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651654301/debbie.codes/blog/2022/twitter-messages-wasim_2x_tum5f7.png",[11,28262,28263],{},"On February the 10th I finished completing the form and at the end it asked if I was able to work in America, which I answered no, and it said the position was only open to US. I had no idea if it was even going to be a possibility but to be honest the Google one was the same. I had been told that sometimes they just need to add a location because the system requires it and as always you have nothing to lose by just applying and giving these things a go.",[11,28265,28266],{},"On the 17th February, I received an email from the Hiring Manager at Microsoft. At this stage I was still waiting on the no from Google so this was good news to have another opportunity at proving myself.",[138,28268,28270],{"id":28269},"interview-with-the-hiring-manager","Interview with the Hiring Manager",[11,28272,28273],{},"On the 24th January I had my first interview with the hiring manager. The interview went really well and was more chatting about the role itself and what I was looking for in such a role. There was no one trying to catch me out and I was delighted to hear that there was no white board interview. Instead I would have to prepare a presentation and be asked questions after it followed by three 1-1 interviews. He also told me to expect to hear back in about 3 weeks.",[11,28275,28276],{},"At this stage I had stopped studying data structures and algorithms and was now looking into Playwright. But I was also working fulltime, finishing off conference talks and a friends wedding to attend abroad, so I really didn't have that much time to dedicate to it.",[138,28278,28280],{"id":28279},"the-recruiter-email","The Recruiter Email",[11,28282,28283],{},"On the 12th March I received a recruiter email asking me some questions and what was the best phone number to reach me on. Then on the 15th March I received a system email from the Action Centre which was so nice to receive especially reading the words \"We were impressed by your background\". It really was very exciting.",[11,28285,28286],{},[121,28287],{"alt":28288,"src":28289},"email from Microsoft saying they were impressed with my background","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651662171/debbie.codes/blog/2022/action-centre-email_2x_agidoq.png",[11,28291,28292],{},"That same day a recruiter reached out asking me for 2-3 preferred dates and time zone for the interview and they requested interviews take place within the next two weeks if possible. Now things were happening fast.",[138,28294,28296],{"id":28295},"time-to-write-the-talk","Time to Write the Talk",[11,28298,28299],{},"And so interviews were planned for the 21st and 22nd of March. I now had a week to get a presentation done. I do have a lot of experience in creating talks but creating slides is always hard for me. I always spend so much time on research and then think out what story I want to tell and then I write it out in almost a blog format with screenshots and code examples. Then I can finally turn it into an actual presentation that looks somewhat presentable, normally using an already created theme.",[11,28301,28302],{},"For the research I had watched every video available on Playwright and took down lots of notes plus tried to re-produce everything I saw on my own mini demo project I had created. I could never talk about something I don't understand or haven't already tried. I also really need to be comfortable with what I am explaining as I don't use speaker notes when giving a talk. I use them for prep and rehearsal but not when giving the actual talk.",[11,28304,28305],{},"That weekend I cancelled all plans to celebrate Saint Patrick's Day and literally just spent the weekend working on slides. I had no idea if my talk would be good enough or if I knew enough and I was quite nervous as I knew Scott Hanselman, one of my tech idols, would be part of the interview panel.",[200,28307,28309],{"id":28308},"receiving-great-support","Receiving Great Support",[11,28311,28312],{},"Actually it was on the 17th March, Saint Patrick's Day, that the hiring manager James Montemagno reached out to me to make sure I understood how the presentation interview worked and if I had any questions. Of course I had questions. I always have questions and doubts.",[11,28314,28315],{},"James not only answered my questions but also assured me not to be nervous about presenting in front of Scott Hanselman and that he really is just a normal person. Just being open and honest about your fears really helps.",[11,28317,28318],{},"The simple email I received from James, the hiring manager, just before the interview saying \"You got this!\" really helped keep me calm. I guess it was because he was rooting for me rather than trying to find a way to fail me which was my experience in previous interviews.",[138,28320,28322],{"id":28321},"the-presentation","The Presentation",[11,28324,28325],{},"On the 21st March I gave my presentation on Playwright. The interview panel asked me if I wanted the cameras on or off and I requested on. Giving a talk and seeing peoples faces and reactions really helps you see if your talk is going well or not and helps you judge if you need to explain things further or if what you wanted to say was understood.",[11,28327,28328],{},"I later found out that Scott Hanselman had kept his camera off so that I wouldn't be so nervous. He only turned it on after the presentation for the Q and A. And I think it worked as because I didn't see him I just presumed he wasn't there so it was one less thing to be nervous about.",[11,28330,28331],{},"The presentation went really well although I spoke way too fast and did it in less time than I should. I was nervous and I guess it is to be expected. But it felt good. I felt I had given a good presentation and I had remembered everything I had planned to say. After the presentation I was asked lots of questions by the interview panel which consisted of 6 people, I believe.",[11,28333,28334],{},"The questions were not easy but I had a lot of experience in standing on a stage and being asked difficult questions about my talk and the product so I wasn't really worried and just had fun as if I was at a conference. The interview panel acted as if they were attendees at a conference and at no stage did it feel like I was actually being interviewed at all. I really enjoyed the interview process. And it was the first time I had ever had an interview where women were on the panel.",[138,28336,28338],{"id":28337},"the-1-1s","The 1-1's",[11,28340,28341],{},"My 1-1 interviews were the next day. The great thing about the process in Microsoft is that they tell you who you are going to be interviewed by. This meant that I could Google them to find out more about them. Although this sounds a bit like being a stalker, it's actually not. What it led me to do was get to know them better so I could use my time more wisely, such as read a blog post they had written so I could bring up certain questions or knew who the right people was to talk about certain things such as imposter syndrome in the workplace. I had my questions prepared of what I wanted to ask each person and that made me more prepared for the interviews.",[11,28343,28344],{},"My interviews felt more like podcast interviews. We just chatted, and although obviously they were asking me questions and taking notes, I never felt like they were trying to ask the difficult questions that trip you up and make you feel stupid. It felt comfortable.",[11,28346,28347,28348,28353],{},"Again ",[15,28349,28352],{"href":28350,"rel":28351},"https://careers.microsoft.com/us/en/diversityandinclusion",[19],"diversity"," shone here as one interview was with a white guy, one with a black guy and one with a women. I have never in all my years in tech, and the many interviews I have been through, have I been interviewed by either a person of color or by a women. This was a first and it was really nice to have such a diverse interview panel.",[11,28355,28356],{},"There were many reasons why I wanted to work at Microsoft and they remain the same but now it's the people that really make me want to work there even more. If my interview is like this then imagine what it will be like to actually work there. Now more than anything I really wanted to get the job.",[138,28358,28360],{"id":28359},"getting-the-yes","Getting the Yes",[11,28362,28363],{},"The hiring team were very quick at getting back to me and on the 26th of March I had the recruiter call to tell me they would like to make me an offer. Now came the tricky part. As the job was US based and I live in Spain, it now had to be moved to the Spanish offices before the actual offer on paper was made. This is due to different currency, benefits etc so I now had an offer of a job but had no idea what the salary would be.",[11,28365,28366],{},"It took almost 2 weeks for recruiters in Spain to pick up from the US offices and reach out to me which was pretty quick. I was in Ireland at the time when I got the call and the offer explaining the salary and benefits etc. I have been contracted as a freelance in my last two positions so this is really my first job that comes with all this kinda thing so I was taking so many notes and just taking it all in. It was actually really nice to get this call while in Ireland as I then got to celebrate the offer with my family.",[138,28368,28370],{"id":28369},"the-background-checks","The Background Checks",[11,28372,28373],{},"This was by far the hardest part of all. You now have an offer. You have accepted the offer but now you are told to not hand in your notice until all background checks are completed. I have never in my life had a background check before and although I read all the information that they had sent me and watched the video explaining the process I was still not prepared for it.",[11,28375,28376],{},"I really just thought that as I have never been in trouble with the law in my life that these checks would be super simple and complete within a few days. And boy was I wrong. The checks went back for 5 years of employment. In the last 5 years I had had 5 jobs plus volunteering work and a university degree. Not just that but some of my employment was contract work meaning I legally had my own business which is a whole other lot of paper work which had to be sent.",[200,28378,28380],{"id":28379},"week-1","Week 1",[11,28382,28383],{},"I was checking the online portal every few hours of every day and it would constantly go in orange where I needed to submit another document. At one point we were driving to a restaurant and we turned around to go home to upload another document. I just thought that the quicker I upload things the quicker it would be at finishing the process. Checking the portal was the first thing I did in the morning and the last thing I did at night.",[200,28385,28387],{"id":28386},"week-2","Week 2",[11,28389,28390],{},"Week 2 was quieter. It stopped asking me for documents and just stayed the same color. I am not sure which is worse, asking me to submit more or just no idea what is happening. Every day I would wake up thinking perhaps today is the day it passes. But no. Week 2 was just another week of testing my patience.",[200,28392,28394],{"id":28393},"week-3","Week 3",[11,28396,28397],{},"Week 3 was harder. It felt like this process was never going to end. They tell you it can take up to 3 weeks but you never expect it to take as long as that and you start to worry that something must be wrong if it is taking so long. Your mind plays games with you and you start to think what if it doesn't work out, what if it doesn't pass, I'm not going to be able to work for Microsoft.",[11,28399,28400],{},"Patience on week 3 was hard. Not suffering an anxiety attack on week 3 was hard. But in the middle of the week I finally got news that the report was finished but there were a few things they couldn't verify and I needed to find more documents to prove my start and end dates for previous companies. Once I had uploaded about 20 documents I just thought to myself that I can't do anymore more. My documents are solid but if they are not good enough then there is nothing else I can do.",[138,28402,28404],{"id":28403},"finally","Finally :)",[11,28406,28407],{},"The Microsoft team were very quick to get back to me thanking me for the documents and saying that my background checks had now passed. I was officially not a criminal. This was such a relief like you can not imagine. I mean I never thought I was a criminal but I was just so afraid of something going wrong, something stopping my dream from coming true.",[11,28409,28410],{},"I could now hand in my notice at my job and get my actual starting date.",[11,28412,28413],{},[121,28414],{"alt":28415,"src":28416},"admin portal showing that I am hired","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1651677083/debbie.codes/blog/2022/hired-microsoft_2x_ipaos9.png",[11,28418,28419],{},"I started the process on the 4th of February. All in all it is not a long process when you think about what is involved but for someone who has never been through such a process it feels like forever and was by far the longest process I have ever had to go through.",[11,28421,28422],{},"But I am glad to say that I made it and on the 16th of May, I had my first day at Microsoft and it was just so amazing, even better than I had imagined.",[11,28424,28425,28426,28431],{},"Next it could be you. If it is your dream then I highly encourage you to just give it a go and ",[15,28427,28430],{"href":28428,"rel":28429},"https://careers.microsoft.com/us/en",[19],"apply for one of the roles",". Interview processes are never easy but the end result is totally worth it.",{"title":307,"searchDepth":748,"depth":748,"links":28433},[28434,28435,28442,28446],{"id":28030,"depth":748,"text":28031},{"id":28040,"depth":748,"text":28041,"children":28436},[28437,28438,28439,28440,28441],{"id":28047,"depth":756,"text":28048},{"id":28063,"depth":756,"text":28064},{"id":28088,"depth":756,"text":28089},{"id":28147,"depth":756,"text":28148},{"id":28176,"depth":756,"text":28177},{"id":28202,"depth":748,"text":28203,"children":28443},[28444,28445],{"id":28218,"depth":756,"text":28219},{"id":28228,"depth":756,"text":28229},{"id":28241,"depth":748,"text":28242,"children":28447},[28448,28449,28450,28451,28452,28453,28454,28455],{"id":28269,"depth":756,"text":28270},{"id":28279,"depth":756,"text":28280},{"id":28295,"depth":756,"text":28296},{"id":28321,"depth":756,"text":28322},{"id":28337,"depth":756,"text":28338},{"id":28359,"depth":756,"text":28360},{"id":28369,"depth":756,"text":28370},{"id":28403,"depth":756,"text":28404},"2022-05-19","When a recruiter from Meta, from America, had called me, it just opened my eyes to the possibility that maybe just maybe I was actually good enough to work for the big tech companies.","v1651679092/debbie.codes/blog/2022/data-structures_2x_kvks59.png",{"ogImage":28460},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651679092/debbie.codes/blog/2022/data-structures_2x_kvks59.png","/blog/interviewing-with-the-big-tech-companies",{"title":28025,"description":28457},"blog/interviewing-with-the-big-tech-companies",[3464],"hx7g2ticMZQ4NXp8pkE-QmMg8S_WceKo0VuRLPE3_TE",{"id":28467,"title":28468,"body":28469,"canonical":788,"date":28473,"description":28474,"extension":786,"featured":787,"image":28475,"meta":28476,"navigation":790,"ogimage":788,"path":28478,"provider":3460,"published":787,"seo":28479,"stem":28480,"tags":28481,"url":28482,"__hash__":28483},"blog/blog/javascript-workshop-with-kyle-simpson.md","JavaScript Workshop with Kyle Simpson",{"type":8,"value":28470,"toc":28471},[],{"title":307,"searchDepth":748,"depth":748,"links":28472},[],"2018-10-15","It has been a couple of months since Kyle, author of the “You Don’t Know JS” series, visited our trivago headquarters and gave us a wonderful JavaScript workshop. It was such an enjoyable week, being able to meet Kyle in person and walk through his thinking behind the materials.","v1607270365/debbie.codes/blog/trivago_w7istq",{"platform":28477},"Trivago","/blog/javascript-workshop-with-kyle-simpson",{"title":28468,"description":28474},"blog/javascript-workshop-with-kyle-simpson",[3464],"https://tech.trivago.com/2018/10/15/javascript-workshop-with-kyle-simpson/","9XKu1VJt9UKBJvqoQPmDZtyfBBUduSxkUcU5qjp5lwI",{"id":28485,"title":28486,"body":28487,"canonical":788,"date":28868,"description":28869,"extension":786,"featured":787,"image":28870,"meta":28871,"navigation":790,"ogimage":788,"path":28873,"provider":5235,"published":790,"seo":28874,"stem":28875,"tags":28876,"url":788,"__hash__":28877},"blog/blog/js-array-at-method.md","The JavaScript Array.at() method",{"type":8,"value":28488,"toc":28855},[28489,28496,28499,28503,28506,28510,28513,28527,28531,28538,28557,28561,28564,28567,28586,28590,28593,28613,28616,28622,28641,28644,28646,28649,28810,28819,28825,28829,28837,28852],[11,28490,28491,28492,28495],{},"Often we want to return a single item from an array. There are a few methods of doing this including the ",[179,28493,28494],{},"at(index)"," method which returns the item at a given index. It takes both positive and negative values where negatives values count back from the last item in the array.",[11,28497,28498],{},"Let's take a look and compare this method with the methods we might be more familiar with.",[23,28500,28502],{"id":28501},"getting-the-index-of-the-first-item-in-an-array","Getting the index of the first item in an array",[11,28504,28505],{},"Using square bracket notation is also still perfectly fine and to get the first item in an array you can do:",[138,28507,28509],{"id":28508},"using-the-array-length-method","Using the array length method",[11,28511,28512],{},"You an use the square brackets passing in the index of 0 to get the first item in the array.",[299,28514,28516],{"className":8734,"code":28515,"language":8736,"meta":307,"style":307},"array[0]\n",[179,28517,28518],{"__ignoreMap":307},[1736,28519,28520,28523,28525],{"class":1738,"line":1739},[1736,28521,28522],{"class":1912},"array[",[1736,28524,1290],{"class":1918},[1736,28526,8420],{"class":1912},[138,28528,28530],{"id":28529},"using-the-at-method","Using the at method",[11,28532,28533,28534,28537],{},"You can also use the ",[179,28535,28536],{},"at()"," method passing in the index of 0 to get the first item in the array.",[299,28539,28541],{"className":8734,"code":28540,"language":8736,"meta":307,"style":307},"array.at(0)\n",[179,28542,28543],{"__ignoreMap":307},[1736,28544,28545,28548,28551,28553,28555],{"class":1738,"line":1739},[1736,28546,28547],{"class":1912},"array.",[1736,28549,28550],{"class":2674},"at",[1736,28552,7751],{"class":1912},[1736,28554,1290],{"class":1918},[1736,28556,7045],{"class":1912},[23,28558,28560],{"id":28559},"getting-the-index-of-the-last-item-in-an-array","Getting the index of the last item in an array",[138,28562,28509],{"id":28563},"using-the-array-length-method-1",[11,28565,28566],{},"The real difference is when you want to get the last number in the array. It is very common to use the length property of an array and subtract 1, to get the last item in the array. We use a -1 because arrays start at 0 and not 1.",[299,28568,28570],{"className":8734,"code":28569,"language":8736,"meta":307,"style":307},"array[array.length - 1]\n",[179,28571,28572],{"__ignoreMap":307},[1736,28573,28574,28577,28579,28582,28584],{"class":1738,"line":1739},[1736,28575,28576],{"class":1912},"array[array.",[1736,28578,18450],{"class":1918},[1736,28580,28581],{"class":4866}," -",[1736,28583,9722],{"class":1918},[1736,28585,8420],{"class":1912},[138,28587,28589],{"id":28588},"using-the-slice-method","Using the slice method",[11,28591,28592],{},"Or you could use the slice method where you can slice from one index to another to get the value you want. However this method will return an array which might not necessarily be what you want.",[299,28594,28596],{"className":8734,"code":28595,"language":8736,"meta":307,"style":307},"array.slice(-1)\n",[179,28597,28598],{"__ignoreMap":307},[1736,28599,28600,28602,28605,28607,28609,28611],{"class":1738,"line":1739},[1736,28601,28547],{"class":1912},[1736,28603,28604],{"class":2674},"slice",[1736,28606,7751],{"class":1912},[1736,28608,9419],{"class":4866},[1736,28610,10249],{"class":1918},[1736,28612,7045],{"class":1912},[138,28614,28530],{"id":28615},"using-the-at-method-1",[11,28617,28618,28619,891],{},"Although there is nothing wrong with using the method above we can use a shorter syntax using the at() method and passing in the value of -1 without having to use the ",[179,28620,28621],{},"array.length",[299,28623,28625],{"className":8734,"code":28624,"language":8736,"meta":307,"style":307},"array.at(-1)\n",[179,28626,28627],{"__ignoreMap":307},[1736,28628,28629,28631,28633,28635,28637,28639],{"class":1738,"line":1739},[1736,28630,28547],{"class":1912},[1736,28632,28550],{"class":2674},[1736,28634,7751],{"class":1912},[1736,28636,9419],{"class":4866},[1736,28638,10249],{"class":1918},[1736,28640,7045],{"class":1912},[11,28642,28643],{},"Again both methods are fine so use the one you prefer.",[23,28645,22290],{"id":22289},[11,28647,28648],{},"To see a real example in use, let's create an array of food and then find the index of the first and last item in the array.",[299,28650,28652],{"className":8734,"code":28651,"language":8736,"meta":307,"style":307},"const food = ['pizza', 'burgers', 'sushi', 'steak', 'salad']\n\nconsole.log(food[0]) // pizza\n\nconsole.log(food.at(0)) // pizza\n\nconsole.log(food[food.length - 1]) // salad\n\nconsole.log(food.slice(-1)) // [salad]\n\nconsole.log(food.at(-1)) // salad\n",[179,28653,28654,28690,28694,28713,28717,28737,28741,28761,28765,28786,28790],{"__ignoreMap":307},[1736,28655,28656,28658,28661,28663,28665,28668,28670,28673,28675,28678,28680,28683,28685,28688],{"class":1738,"line":1739},[1736,28657,5029],{"class":4866},[1736,28659,28660],{"class":1918}," food",[1736,28662,4911],{"class":4866},[1736,28664,8409],{"class":1912},[1736,28666,28667],{"class":1935},"'pizza'",[1736,28669,829],{"class":1912},[1736,28671,28672],{"class":1935},"'burgers'",[1736,28674,829],{"class":1912},[1736,28676,28677],{"class":1935},"'sushi'",[1736,28679,829],{"class":1912},[1736,28681,28682],{"class":1935},"'steak'",[1736,28684,829],{"class":1912},[1736,28686,28687],{"class":1935},"'salad'",[1736,28689,8420],{"class":1912},[1736,28691,28692],{"class":1738,"line":748},[1736,28693,1747],{"emptyLinePlaceholder":790},[1736,28695,28696,28699,28702,28705,28707,28710],{"class":1738,"line":756},[1736,28697,28698],{"class":1912},"console.",[1736,28700,28701],{"class":2674},"log",[1736,28703,28704],{"class":1912},"(food[",[1736,28706,1290],{"class":1918},[1736,28708,28709],{"class":1912},"]) ",[1736,28711,28712],{"class":6820},"// pizza\n",[1736,28714,28715],{"class":1738,"line":1755},[1736,28716,1747],{"emptyLinePlaceholder":790},[1736,28718,28719,28721,28723,28726,28728,28730,28732,28735],{"class":1738,"line":1761},[1736,28720,28698],{"class":1912},[1736,28722,28701],{"class":2674},[1736,28724,28725],{"class":1912},"(food.",[1736,28727,28550],{"class":2674},[1736,28729,7751],{"class":1912},[1736,28731,1290],{"class":1918},[1736,28733,28734],{"class":1912},")) ",[1736,28736,28712],{"class":6820},[1736,28738,28739],{"class":1738,"line":1767},[1736,28740,1747],{"emptyLinePlaceholder":790},[1736,28742,28743,28745,28747,28750,28752,28754,28756,28758],{"class":1738,"line":1772},[1736,28744,28698],{"class":1912},[1736,28746,28701],{"class":2674},[1736,28748,28749],{"class":1912},"(food[food.",[1736,28751,18450],{"class":1918},[1736,28753,28581],{"class":4866},[1736,28755,9722],{"class":1918},[1736,28757,28709],{"class":1912},[1736,28759,28760],{"class":6820},"// salad\n",[1736,28762,28763],{"class":1738,"line":1778},[1736,28764,1747],{"emptyLinePlaceholder":790},[1736,28766,28767,28769,28771,28773,28775,28777,28779,28781,28783],{"class":1738,"line":1784},[1736,28768,28698],{"class":1912},[1736,28770,28701],{"class":2674},[1736,28772,28725],{"class":1912},[1736,28774,28604],{"class":2674},[1736,28776,7751],{"class":1912},[1736,28778,9419],{"class":4866},[1736,28780,10249],{"class":1918},[1736,28782,28734],{"class":1912},[1736,28784,28785],{"class":6820},"// [salad]\n",[1736,28787,28788],{"class":1738,"line":1790},[1736,28789,1747],{"emptyLinePlaceholder":790},[1736,28791,28792,28794,28796,28798,28800,28802,28804,28806,28808],{"class":1738,"line":1796},[1736,28793,28698],{"class":1912},[1736,28795,28701],{"class":2674},[1736,28797,28725],{"class":1912},[1736,28799,28550],{"class":2674},[1736,28801,7751],{"class":1912},[1736,28803,9419],{"class":4866},[1736,28805,10249],{"class":1918},[1736,28807,28734],{"class":1912},[1736,28809,28760],{"class":6820},[11,28811,28812,28813,28818],{},"Paste it in your console and see for yourself or play around with the indexes to get different results Or checkout the ",[15,28814,28817],{"href":28815,"rel":28816},"https://codepen.io/debs-obrien/pen/vYdXxdy",[19],"codepen"," I created.",[11,28820,28821,28822,28824],{},"Again all methods work perfectly fine so choose the one you prefer. The ",[179,28823,28536],{}," method seems quite nice and easy to remember too and it has full browser support too. (Unless you need Internet Explorer or Opera for Android).",[23,28826,28828],{"id":28827},"learn-more","Learn More",[11,28830,28831,28832],{},"To read more about it check out the MDn docs for ",[15,28833,28836],{"href":28834,"rel":28835},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at",[19],"Array.prototype.at()",[70,28838,28839,28846],{},[73,28840,28841,28842],{},"Check out my post on the ",[15,28843,28845],{"href":28844},"/blog/js-array-filter-method","array filter method",[73,28847,28841,28848],{},[15,28849,28851],{"href":28850},"/blog/js-array-map-method","array map method",[2011,28853,28854],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":28856},[28857,28861,28866,28867],{"id":28501,"depth":748,"text":28502,"children":28858},[28859,28860],{"id":28508,"depth":756,"text":28509},{"id":28529,"depth":756,"text":28530},{"id":28559,"depth":748,"text":28560,"children":28862},[28863,28864,28865],{"id":28563,"depth":756,"text":28509},{"id":28588,"depth":756,"text":28589},{"id":28615,"depth":756,"text":28530},{"id":22289,"depth":748,"text":22290},{"id":28827,"depth":748,"text":28828},"2022-05-12","Often we want to return a single item from an array. There are a few methods of doing this including the `at(index)` method which returns the item at a given index.","photo-1562755524-cb3e786bee18?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjJ8fGF0fGVufDB8MHwwfHw%3D&auto=format&fit=crop&w=800&q=60",{"ogImage":28872},"https://images.unsplash.com/photo-1562755524-cb3e786bee18?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjJ8fGF0fGVufDB8MHwwfHw%3D&auto=format&fit=crop&w=800&q=60","/blog/js-array-at-method",{"title":28486,"description":28869},"blog/js-array-at-method",[5637],"1KtYdfwxKwSAgqJXmMqwQSWzy_DOZWoYmb_f5AX7pa0",{"id":28879,"title":28880,"body":28881,"canonical":788,"date":29397,"description":29398,"extension":786,"featured":787,"image":29399,"meta":29400,"navigation":790,"ogimage":788,"path":28844,"provider":5235,"published":790,"seo":29402,"stem":29403,"tags":29404,"url":788,"__hash__":29405},"blog/blog/js-array-filter-method.md","The JavaScript Array.filter() method",{"type":8,"value":28882,"toc":29385},[28883,28890,28941,28945,28950,28953,28964,28968,29021,29025,29028,29071,29075,29078,29119,29123,29126,29164,29168,29171,29206,29210,29213,29216,29242,29244,29251,29346,29353,29355,29382],[11,28884,28885,28886,28889],{},"Sometimes we have an array but we want to return only a select few items from the array. For example, we can use the ",[179,28887,28888],{},"array.filter()"," method to filter an array of people to only find the female characters of the array.",[299,28891,28893],{"className":8734,"code":28892,"language":8736,"meta":307,"style":307},"const people = [\n  { name: 'Debbie', gender: 'female' },\n  { name: 'Josh', gender: 'male' }\n]\n",[179,28894,28895,28907,28923,28937],{"__ignoreMap":307},[1736,28896,28897,28899,28902,28904],{"class":1738,"line":1739},[1736,28898,5029],{"class":4866},[1736,28900,28901],{"class":1918}," people",[1736,28903,4911],{"class":4866},[1736,28905,28906],{"class":1912}," [\n",[1736,28908,28909,28912,28915,28918,28921],{"class":1738,"line":748},[1736,28910,28911],{"class":1912},"  { name: ",[1736,28913,28914],{"class":1935},"'Debbie'",[1736,28916,28917],{"class":1912},", gender: ",[1736,28919,28920],{"class":1935},"'female'",[1736,28922,7197],{"class":1912},[1736,28924,28925,28927,28930,28932,28935],{"class":1738,"line":756},[1736,28926,28911],{"class":1912},[1736,28928,28929],{"class":1935},"'Josh'",[1736,28931,28917],{"class":1912},[1736,28933,28934],{"class":1935},"'male'",[1736,28936,21922],{"class":1912},[1736,28938,28939],{"class":1738,"line":1755},[1736,28940,8420],{"class":1912},[23,28942,28944],{"id":28943},"how-the-filter-method-works","How the filter method works",[11,28946,2506,28947,28949],{},[179,28948,27263],{}," method calls a callback function once for each element in an array and constructs a new array for all the values that pass the test provided in this callback function.",[11,28951,28952],{},"The function takes 3 arguments,",[70,28954,28955,28958,28961],{},[73,28956,28957],{},"the current value, which is the current value we are iterating over",[73,28959,28960],{},"the index, what iteration we are on",[73,28962,28963],{},"the original array on which filter is called",[138,28965,28967],{"id":28966},"returns-female-values","Returns female values",[299,28969,28971],{"className":8734,"code":28970,"language":8736,"meta":307,"style":307},"people.filter((currentValue, index, originalArray) => {\n  // decide who should be returned\n  return currentValue.gender === 'female'\n})\n",[179,28972,28973,29000,29005,29017],{"__ignoreMap":307},[1736,28974,28975,28978,28980,28982,28985,28987,28989,28991,28994,28996,28998],{"class":1738,"line":1739},[1736,28976,28977],{"class":1912},"people.",[1736,28979,14507],{"class":2674},[1736,28981,14369],{"class":1912},[1736,28983,28984],{"class":5036},"currentValue",[1736,28986,829],{"class":1912},[1736,28988,14738],{"class":5036},[1736,28990,829],{"class":1912},[1736,28992,28993],{"class":5036},"originalArray",[1736,28995,8939],{"class":1912},[1736,28997,7013],{"class":4866},[1736,28999,4914],{"class":1912},[1736,29001,29002],{"class":1738,"line":748},[1736,29003,29004],{"class":6820},"  // decide who should be returned\n",[1736,29006,29007,29009,29012,29014],{"class":1738,"line":756},[1736,29008,6685],{"class":4866},[1736,29010,29011],{"class":1912}," currentValue.gender ",[1736,29013,7786],{"class":4866},[1736,29015,29016],{"class":1935}," 'female'\n",[1736,29018,29019],{"class":1738,"line":1755},[1736,29020,10582],{"class":1912},[138,29022,29024],{"id":29023},"exclude-the-original-array","Exclude the original array",[11,29026,29027],{},"We can exclude the original array in our callback function if we are not using it. We may want to use it to push something to the array before we perform the test of what to return but if not we can simply remove it.",[299,29029,29031],{"className":8734,"code":29030,"language":8736,"meta":307,"style":307},"people.filter((currentValue, index) => {\n  // decide who should be returned\n  return currentValue.gender === 'female'\n})\n",[179,29032,29033,29053,29057,29067],{"__ignoreMap":307},[1736,29034,29035,29037,29039,29041,29043,29045,29047,29049,29051],{"class":1738,"line":1739},[1736,29036,28977],{"class":1912},[1736,29038,14507],{"class":2674},[1736,29040,14369],{"class":1912},[1736,29042,28984],{"class":5036},[1736,29044,829],{"class":1912},[1736,29046,14738],{"class":5036},[1736,29048,8939],{"class":1912},[1736,29050,7013],{"class":4866},[1736,29052,4914],{"class":1912},[1736,29054,29055],{"class":1738,"line":748},[1736,29056,29004],{"class":6820},[1736,29058,29059,29061,29063,29065],{"class":1738,"line":756},[1736,29060,6685],{"class":4866},[1736,29062,29011],{"class":1912},[1736,29064,7786],{"class":4866},[1736,29066,29016],{"class":1935},[1736,29068,29069],{"class":1738,"line":1755},[1736,29070,10582],{"class":1912},[138,29072,29074],{"id":29073},"naming-the-current-value","Naming the current value",[11,29076,29077],{},"Normally we name the currentValue the singular of the array so in this case we should name it person.",[299,29079,29081],{"className":8734,"code":29080,"language":8736,"meta":307,"style":307},"people.filter((person, index) => {\n  return person.gender === 'female'\n})\n",[179,29082,29083,29104,29115],{"__ignoreMap":307},[1736,29084,29085,29087,29089,29091,29094,29096,29098,29100,29102],{"class":1738,"line":1739},[1736,29086,28977],{"class":1912},[1736,29088,14507],{"class":2674},[1736,29090,14369],{"class":1912},[1736,29092,29093],{"class":5036},"person",[1736,29095,829],{"class":1912},[1736,29097,14738],{"class":5036},[1736,29099,8939],{"class":1912},[1736,29101,7013],{"class":4866},[1736,29103,4914],{"class":1912},[1736,29105,29106,29108,29111,29113],{"class":1738,"line":748},[1736,29107,6685],{"class":4866},[1736,29109,29110],{"class":1912}," person.gender ",[1736,29112,7786],{"class":4866},[1736,29114,29016],{"class":1935},[1736,29116,29117],{"class":1738,"line":756},[1736,29118,10582],{"class":1912},[138,29120,29122],{"id":29121},"using-the-arrow-function","Using the arrow function",[11,29124,29125],{},"We can also use the arrow function instead",[299,29127,29128],{"className":8734,"code":29080,"language":8736,"meta":307,"style":307},[179,29129,29130,29150,29160],{"__ignoreMap":307},[1736,29131,29132,29134,29136,29138,29140,29142,29144,29146,29148],{"class":1738,"line":1739},[1736,29133,28977],{"class":1912},[1736,29135,14507],{"class":2674},[1736,29137,14369],{"class":1912},[1736,29139,29093],{"class":5036},[1736,29141,829],{"class":1912},[1736,29143,14738],{"class":5036},[1736,29145,8939],{"class":1912},[1736,29147,7013],{"class":4866},[1736,29149,4914],{"class":1912},[1736,29151,29152,29154,29156,29158],{"class":1738,"line":748},[1736,29153,6685],{"class":4866},[1736,29155,29110],{"class":1912},[1736,29157,7786],{"class":4866},[1736,29159,29016],{"class":1935},[1736,29161,29162],{"class":1738,"line":756},[1736,29163,10582],{"class":1912},[138,29165,29167],{"id":29166},"removing-the-index-and-parenthesis","Removing the index and parenthesis",[11,29169,29170],{},"As we are not using the index we can get rid of that. We can also get rid of the parenthesis as we don't need them if there is only one argument in an arrow function",[299,29172,29174],{"className":8734,"code":29173,"language":8736,"meta":307,"style":307},"people.filter((person) => {\n  return person.gender === 'female'\n})\n",[179,29175,29176,29192,29202],{"__ignoreMap":307},[1736,29177,29178,29180,29182,29184,29186,29188,29190],{"class":1738,"line":1739},[1736,29179,28977],{"class":1912},[1736,29181,14507],{"class":2674},[1736,29183,14369],{"class":1912},[1736,29185,29093],{"class":5036},[1736,29187,8939],{"class":1912},[1736,29189,7013],{"class":4866},[1736,29191,4914],{"class":1912},[1736,29193,29194,29196,29198,29200],{"class":1738,"line":748},[1736,29195,6685],{"class":4866},[1736,29197,29110],{"class":1912},[1736,29199,7786],{"class":4866},[1736,29201,29016],{"class":1935},[1736,29203,29204],{"class":1738,"line":756},[1736,29205,10582],{"class":1912},[138,29207,29209],{"id":29208},"removing-the-brackets-and-return-keyword","Removing the brackets and return keyword",[11,29211,29212],{},"And as it is just one line we are returning we can remove the return and the curly brackets and have it all on the one line keeping it very short. This can take a bit of getting used to as it is not as easy to read as the previous examples but many people will use this format.",[11,29214,29215],{},"Think of it as, for the array of people filter every person and return the ones that have a gender of female.",[299,29217,29219],{"className":8734,"code":29218,"language":8736,"meta":307,"style":307},"people.filter(person => person.gender === 'female')\n",[179,29220,29221],{"__ignoreMap":307},[1736,29222,29223,29225,29227,29229,29231,29233,29235,29237,29240],{"class":1738,"line":1739},[1736,29224,28977],{"class":1912},[1736,29226,14507],{"class":2674},[1736,29228,7751],{"class":1912},[1736,29230,29093],{"class":5036},[1736,29232,10208],{"class":4866},[1736,29234,29110],{"class":1912},[1736,29236,7786],{"class":4866},[1736,29238,29239],{"class":1935}," 'female'",[1736,29241,7045],{"class":1912},[23,29243,22290],{"id":22289},[11,29245,29246,29247,29250],{},"The great thing about filter is that it does not mutate the original array. So if you ",[179,29248,29249],{},"console.log(people) "," you will still get all the people from the people array as filter creates a new array. Therefore we can store our new filter in a const.",[299,29252,29254],{"className":8734,"code":29253,"language":8736,"meta":307,"style":307},"const people = [\n  { name: 'Debbie', gender: 'female' },\n  { name: 'Josh', gender: 'male' }\n]\nconst women = people.filter(person => person.gender === 'female')\nconsole.log(people) // [{ name: 'Debbie', gender: 'female' },{ name: 'Josh', gender: 'male' }]\nconsole.log(women) // [{ name: 'Debbie', gender: 'female' }]\n",[179,29255,29256,29266,29278,29290,29294,29322,29334],{"__ignoreMap":307},[1736,29257,29258,29260,29262,29264],{"class":1738,"line":1739},[1736,29259,5029],{"class":4866},[1736,29261,28901],{"class":1918},[1736,29263,4911],{"class":4866},[1736,29265,28906],{"class":1912},[1736,29267,29268,29270,29272,29274,29276],{"class":1738,"line":748},[1736,29269,28911],{"class":1912},[1736,29271,28914],{"class":1935},[1736,29273,28917],{"class":1912},[1736,29275,28920],{"class":1935},[1736,29277,7197],{"class":1912},[1736,29279,29280,29282,29284,29286,29288],{"class":1738,"line":756},[1736,29281,28911],{"class":1912},[1736,29283,28929],{"class":1935},[1736,29285,28917],{"class":1912},[1736,29287,28934],{"class":1935},[1736,29289,21922],{"class":1912},[1736,29291,29292],{"class":1738,"line":1755},[1736,29293,8420],{"class":1912},[1736,29295,29296,29298,29301,29303,29306,29308,29310,29312,29314,29316,29318,29320],{"class":1738,"line":1761},[1736,29297,5029],{"class":4866},[1736,29299,29300],{"class":1918}," women",[1736,29302,4911],{"class":4866},[1736,29304,29305],{"class":1912}," people.",[1736,29307,14507],{"class":2674},[1736,29309,7751],{"class":1912},[1736,29311,29093],{"class":5036},[1736,29313,10208],{"class":4866},[1736,29315,29110],{"class":1912},[1736,29317,7786],{"class":4866},[1736,29319,29239],{"class":1935},[1736,29321,7045],{"class":1912},[1736,29323,29324,29326,29328,29331],{"class":1738,"line":1767},[1736,29325,28698],{"class":1912},[1736,29327,28701],{"class":2674},[1736,29329,29330],{"class":1912},"(people) ",[1736,29332,29333],{"class":6820},"// [{ name: 'Debbie', gender: 'female' },{ name: 'Josh', gender: 'male' }]\n",[1736,29335,29336,29338,29340,29343],{"class":1738,"line":1772},[1736,29337,28698],{"class":1912},[1736,29339,28701],{"class":2674},[1736,29341,29342],{"class":1912},"(women) ",[1736,29344,29345],{"class":6820},"// [{ name: 'Debbie', gender: 'female' }]\n",[11,29347,29348,29349,28818],{},"Paste it in the console to see for yourself and play around with the values or check out the ",[15,29350,28817],{"href":29351,"rel":29352},"https://codepen.io/debs-obrien/pen/OJQRpPW",[19],[23,29354,28828],{"id":28827},[70,29356,29357,29366,29373,29378],{},[73,29358,29359,29360,29365],{},"This article is inspired by ",[15,29361,29364],{"href":29362,"rel":29363},"https://simplygoodwork.com/blog/array-map-javascript-method",[19],"Jake Dohm's video"," which I encourage you to check out.",[73,29367,28831,29368],{},[15,29369,29372],{"href":29370,"rel":29371},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter",[19],"Array.prototype.filter()",[73,29374,28841,29375],{},[15,29376,29377],{"href":28873},"array at method",[73,29379,28841,29380],{},[15,29381,28851],{"href":28850},[2011,29383,29384],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":29386},[29387,29395,29396],{"id":28943,"depth":748,"text":28944,"children":29388},[29389,29390,29391,29392,29393,29394],{"id":28966,"depth":756,"text":28967},{"id":29023,"depth":756,"text":29024},{"id":29073,"depth":756,"text":29074},{"id":29121,"depth":756,"text":29122},{"id":29166,"depth":756,"text":29167},{"id":29208,"depth":756,"text":29209},{"id":22289,"depth":748,"text":22290},{"id":28827,"depth":748,"text":28828},"2022-05-11","The .filter() method in JavaScript creates a new array with all elements that pass the test implemented by the provided function.","photo-1515560570411-00a0026e6086?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8ZmlsdGVyfGVufDB8MHwwfHw%3D&auto=format&fit=crop&w=800&q=60",{"ogImage":29401},"https://images.unsplash.com/photo-1515560570411-00a0026e6086?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NXx8ZmlsdGVyfGVufDB8MHwwfHw%3D&auto=format&fit=crop&w=800&q=60",{"title":28880,"description":29398},"blog/js-array-filter-method",[5637],"yB4B3aq7-N4Ad13u_DTWID0svrb-tLUT2Nr9VqbQaJs",{"id":29407,"title":29408,"body":29409,"canonical":788,"date":29989,"description":29990,"extension":786,"featured":787,"image":29991,"meta":29992,"navigation":790,"ogimage":788,"path":28850,"provider":5235,"published":787,"seo":29993,"stem":29994,"tags":29995,"url":788,"__hash__":29996},"blog/blog/js-array-map-method.md","The JavaScript Array.Map() method",{"type":8,"value":29410,"toc":29986},[29411,29418,29443,29517,29528,29533,29571,29574,29601,29604,29631,29641,29679,29682,29720,29723,29789,29792,29867,29873,29946,29951,29958,29961,29983],[11,29412,29413,29414,29417],{},"Before we dive into the ",[179,29415,29416],{},".map()"," method let's just recap on what is an array. An array is a data structure that contains a group of elements. Think of it like a a big box that inside it has some smaller boxes which can be found by their index. So the box in position 0 will be the first box and box with position 1 will be the second box. Inside these smaller boxes we can have a string of text or numbers or Objects.",[299,29419,29421],{"className":8734,"code":29420,"language":8736,"meta":307,"style":307},"const people = ['first item', 'second item']\n",[179,29422,29423],{"__ignoreMap":307},[1736,29424,29425,29427,29429,29431,29433,29436,29438,29441],{"class":1738,"line":1739},[1736,29426,5029],{"class":4866},[1736,29428,28901],{"class":1918},[1736,29430,4911],{"class":4866},[1736,29432,8409],{"class":1912},[1736,29434,29435],{"class":1935},"'first item'",[1736,29437,829],{"class":1912},[1736,29439,29440],{"class":1935},"'second item'",[1736,29442,8420],{"class":1912},[299,29444,29446],{"className":8734,"code":29445,"language":8736,"meta":307,"style":307},"const people = [\n  {\n    firstName: 'Debbie',\n    lastName: 'O\\'Brien'\n  },\n  {\n    firstName: 'Jake',\n    lastName: 'Dohm'\n  }\n]\n",[179,29447,29448,29458,29462,29471,29485,29489,29493,29502,29509,29513],{"__ignoreMap":307},[1736,29449,29450,29452,29454,29456],{"class":1738,"line":1739},[1736,29451,5029],{"class":4866},[1736,29453,28901],{"class":1918},[1736,29455,4911],{"class":4866},[1736,29457,28906],{"class":1912},[1736,29459,29460],{"class":1738,"line":748},[1736,29461,17704],{"class":1912},[1736,29463,29464,29467,29469],{"class":1738,"line":756},[1736,29465,29466],{"class":1912},"    firstName: ",[1736,29468,28914],{"class":1935},[1736,29470,1939],{"class":1912},[1736,29472,29473,29476,29479,29482],{"class":1738,"line":1755},[1736,29474,29475],{"class":1912},"    lastName: ",[1736,29477,29478],{"class":1935},"'O",[1736,29480,29481],{"class":1918},"\\'",[1736,29483,29484],{"class":1935},"Brien'\n",[1736,29486,29487],{"class":1738,"line":1761},[1736,29488,4929],{"class":1912},[1736,29490,29491],{"class":1738,"line":1767},[1736,29492,17704],{"class":1912},[1736,29494,29495,29497,29500],{"class":1738,"line":1772},[1736,29496,29466],{"class":1912},[1736,29498,29499],{"class":1935},"'Jake'",[1736,29501,1939],{"class":1912},[1736,29503,29504,29506],{"class":1738,"line":1778},[1736,29505,29475],{"class":1912},[1736,29507,29508],{"class":1935},"'Dohm'\n",[1736,29510,29511],{"class":1738,"line":1784},[1736,29512,1971],{"class":1912},[1736,29514,29515],{"class":1738,"line":1790},[1736,29516,8420],{"class":1912},[11,29518,2506,29519,29521,29522,29524,29525,29527],{},[179,29520,29416],{}," method allows you to loop over every element in an array and modify or add to it and then return a different element to take that elements place. However ",[179,29523,29416],{}," does not change the original array. It will always return a new array. We generally use the ",[179,29526,29416],{}," method when you want to add to or modify some data of an array but will have the same amount of elements as the original array.",[11,29529,2506,29530,29532],{},[179,29531,29416],{}," method takes a function which accepts 3 arguments. The first one is the current value, the second is the index and the third one is the original array we are iterating over.",[299,29534,29536],{"className":4894,"code":29535,"language":4896,"meta":307,"style":307},"const names = people.map(function (currentValue, index, allPeople) {})\n",[179,29537,29538],{"__ignoreMap":307},[1736,29539,29540,29542,29545,29547,29549,29551,29553,29555,29557,29559,29561,29563,29565,29568],{"class":1738,"line":1739},[1736,29541,5029],{"class":4866},[1736,29543,29544],{"class":1918}," names",[1736,29546,4911],{"class":4866},[1736,29548,29305],{"class":1912},[1736,29550,14366],{"class":2674},[1736,29552,7751],{"class":1912},[1736,29554,7745],{"class":4866},[1736,29556,1095],{"class":1912},[1736,29558,28984],{"class":5036},[1736,29560,829],{"class":1912},[1736,29562,14738],{"class":5036},[1736,29564,829],{"class":1912},[1736,29566,29567],{"class":5036},"allPeople",[1736,29569,29570],{"class":1912},") {})\n",[11,29572,29573],{},"The function will look over the array of people and will run once for each item in the array. It therefore makes more sense to name the first argument person, the singular version of people. Most of the time you will only need and use the first argument.",[299,29575,29577],{"className":4894,"code":29576,"language":4896,"meta":307,"style":307},"const names = people.map(function (person) {})\n",[179,29578,29579],{"__ignoreMap":307},[1736,29580,29581,29583,29585,29587,29589,29591,29593,29595,29597,29599],{"class":1738,"line":1739},[1736,29582,5029],{"class":4866},[1736,29584,29544],{"class":1918},[1736,29586,4911],{"class":4866},[1736,29588,29305],{"class":1912},[1736,29590,14366],{"class":2674},[1736,29592,7751],{"class":1912},[1736,29594,7745],{"class":4866},[1736,29596,1095],{"class":1912},[1736,29598,29093],{"class":5036},[1736,29600,29570],{"class":1912},[11,29602,29603],{},"We can use an arrow function instead of an anonymous function. And as we only are using one argument we can remove the brackets from person.",[299,29605,29607],{"className":4894,"code":29606,"language":4896,"meta":307,"style":307},"const names = people.map((person) => {})\n",[179,29608,29609],{"__ignoreMap":307},[1736,29610,29611,29613,29615,29617,29619,29621,29623,29625,29627,29629],{"class":1738,"line":1739},[1736,29612,5029],{"class":4866},[1736,29614,29544],{"class":1918},[1736,29616,4911],{"class":4866},[1736,29618,29305],{"class":1912},[1736,29620,14366],{"class":2674},[1736,29622,14369],{"class":1912},[1736,29624,29093],{"class":5036},[1736,29626,8939],{"class":1912},[1736,29628,7013],{"class":4866},[1736,29630,12817],{"class":1912},[11,29632,29633,29634,29637,29638,29640],{},"Inside the function we need to return something. As the ",[179,29635,29636],{},"map()","method calls the function on each item in the array, whatever we return in the function becomes that items value. Therefore if we return ",[179,29639,29093],{}," we will get back exactly what we had in the original array.",[299,29642,29644],{"className":8734,"code":29643,"language":8736,"meta":307,"style":307},"const names = people.map((person) => {\n  return person\n})\n",[179,29645,29646,29668,29675],{"__ignoreMap":307},[1736,29647,29648,29650,29652,29654,29656,29658,29660,29662,29664,29666],{"class":1738,"line":1739},[1736,29649,5029],{"class":4866},[1736,29651,29544],{"class":1918},[1736,29653,4911],{"class":4866},[1736,29655,29305],{"class":1912},[1736,29657,14366],{"class":2674},[1736,29659,14369],{"class":1912},[1736,29661,29093],{"class":5036},[1736,29663,8939],{"class":1912},[1736,29665,7013],{"class":4866},[1736,29667,4914],{"class":1912},[1736,29669,29670,29672],{"class":1738,"line":748},[1736,29671,6685],{"class":4866},[1736,29673,29674],{"class":1912}," person\n",[1736,29676,29677],{"class":1738,"line":756},[1736,29678,10582],{"class":1912},[11,29680,29681],{},"We can return anything we want even though the original array is an array of Objects we could return a string instead. If we return a string with some text then we will get that same string for each item in the array.",[299,29683,29685],{"className":8734,"code":29684,"language":8736,"meta":307,"style":307},"const names = people.map((person) => {\n  return 'Debbie'\n})\n",[179,29686,29687,29709,29716],{"__ignoreMap":307},[1736,29688,29689,29691,29693,29695,29697,29699,29701,29703,29705,29707],{"class":1738,"line":1739},[1736,29690,5029],{"class":4866},[1736,29692,29544],{"class":1918},[1736,29694,4911],{"class":4866},[1736,29696,29305],{"class":1912},[1736,29698,14366],{"class":2674},[1736,29700,14369],{"class":1912},[1736,29702,29093],{"class":5036},[1736,29704,8939],{"class":1912},[1736,29706,7013],{"class":4866},[1736,29708,4914],{"class":1912},[1736,29710,29711,29713],{"class":1738,"line":748},[1736,29712,6685],{"class":4866},[1736,29714,29715],{"class":1935}," 'Debbie'\n",[1736,29717,29718],{"class":1738,"line":756},[1736,29719,10582],{"class":1912},[11,29721,29722],{},"Normally what we want to do is to modify the data or add to it. We could create a variable called fullName and concatenate the values of firstName and lastName.",[299,29724,29726],{"className":8734,"code":29725,"language":8736,"meta":307,"style":307},"const names = people.map((person) => {\n  return {\n    fullName: `${person.firstName} ${person.lastName}`\n  }\n})\n",[179,29727,29728,29750,29756,29781,29785],{"__ignoreMap":307},[1736,29729,29730,29732,29734,29736,29738,29740,29742,29744,29746,29748],{"class":1738,"line":1739},[1736,29731,5029],{"class":4866},[1736,29733,29544],{"class":1918},[1736,29735,4911],{"class":4866},[1736,29737,29305],{"class":1912},[1736,29739,14366],{"class":2674},[1736,29741,14369],{"class":1912},[1736,29743,29093],{"class":5036},[1736,29745,8939],{"class":1912},[1736,29747,7013],{"class":4866},[1736,29749,4914],{"class":1912},[1736,29751,29752,29754],{"class":1738,"line":748},[1736,29753,6685],{"class":4866},[1736,29755,4914],{"class":1912},[1736,29757,29758,29761,29763,29765,29767,29770,29772,29774,29776,29779],{"class":1738,"line":756},[1736,29759,29760],{"class":1912},"    fullName: ",[1736,29762,16967],{"class":1935},[1736,29764,29093],{"class":1912},[1736,29766,891],{"class":1935},[1736,29768,29769],{"class":1912},"firstName",[1736,29771,16972],{"class":1935},[1736,29773,29093],{"class":1912},[1736,29775,891],{"class":1935},[1736,29777,29778],{"class":1912},"lastName",[1736,29780,18693],{"class":1935},[1736,29782,29783],{"class":1738,"line":1755},[1736,29784,1971],{"class":1912},[1736,29786,29787],{"class":1738,"line":1761},[1736,29788,10582],{"class":1912},[11,29790,29791],{},"This will give us a new array with only the fullName returned as that is all we asked for. If however we wanted to have the full name as well as all the rest of the contents in the array we can also easily return them.",[299,29793,29795],{"className":8734,"code":29794,"language":8736,"meta":307,"style":307},"const names = people.map((person) => {\n  return {\n    fullName: `${person.firstName} ${person.lastName}`,\n    firstName: person.firstName,\n    lastName: person.lastName\n  }\n})\n",[179,29796,29797,29819,29825,29849,29854,29859,29863],{"__ignoreMap":307},[1736,29798,29799,29801,29803,29805,29807,29809,29811,29813,29815,29817],{"class":1738,"line":1739},[1736,29800,5029],{"class":4866},[1736,29802,29544],{"class":1918},[1736,29804,4911],{"class":4866},[1736,29806,29305],{"class":1912},[1736,29808,14366],{"class":2674},[1736,29810,14369],{"class":1912},[1736,29812,29093],{"class":5036},[1736,29814,8939],{"class":1912},[1736,29816,7013],{"class":4866},[1736,29818,4914],{"class":1912},[1736,29820,29821,29823],{"class":1738,"line":748},[1736,29822,6685],{"class":4866},[1736,29824,4914],{"class":1912},[1736,29826,29827,29829,29831,29833,29835,29837,29839,29841,29843,29845,29847],{"class":1738,"line":756},[1736,29828,29760],{"class":1912},[1736,29830,16967],{"class":1935},[1736,29832,29093],{"class":1912},[1736,29834,891],{"class":1935},[1736,29836,29769],{"class":1912},[1736,29838,16972],{"class":1935},[1736,29840,29093],{"class":1912},[1736,29842,891],{"class":1935},[1736,29844,29778],{"class":1912},[1736,29846,16103],{"class":1935},[1736,29848,1939],{"class":1912},[1736,29850,29851],{"class":1738,"line":1755},[1736,29852,29853],{"class":1912},"    firstName: person.firstName,\n",[1736,29855,29856],{"class":1738,"line":1761},[1736,29857,29858],{"class":1912},"    lastName: person.lastName\n",[1736,29860,29861],{"class":1738,"line":1767},[1736,29862,1971],{"class":1912},[1736,29864,29865],{"class":1738,"line":1772},[1736,29866,10582],{"class":1912},[11,29868,29869,29870,891],{},"In this example we only have 2 other keys the firstName and lastName but imagine if we had more such as age, address etc. It would be tedious to have to write all of these out. Instead we can use the spread operator. The spread operator expands an iterable object into the list of arguments. We prefix the value with an ellipsis of three dots ",[179,29871,29872],{},"...person",[299,29874,29876],{"className":8734,"code":29875,"language":8736,"meta":307,"style":307},"const names = people.map((person) => {\n  return {\n    fullName: `${person.firstName} ${person.lastName}`,\n    ...person\n  }\n})\n",[179,29877,29878,29900,29906,29930,29938,29942],{"__ignoreMap":307},[1736,29879,29880,29882,29884,29886,29888,29890,29892,29894,29896,29898],{"class":1738,"line":1739},[1736,29881,5029],{"class":4866},[1736,29883,29544],{"class":1918},[1736,29885,4911],{"class":4866},[1736,29887,29305],{"class":1912},[1736,29889,14366],{"class":2674},[1736,29891,14369],{"class":1912},[1736,29893,29093],{"class":5036},[1736,29895,8939],{"class":1912},[1736,29897,7013],{"class":4866},[1736,29899,4914],{"class":1912},[1736,29901,29902,29904],{"class":1738,"line":748},[1736,29903,6685],{"class":4866},[1736,29905,4914],{"class":1912},[1736,29907,29908,29910,29912,29914,29916,29918,29920,29922,29924,29926,29928],{"class":1738,"line":756},[1736,29909,29760],{"class":1912},[1736,29911,16967],{"class":1935},[1736,29913,29093],{"class":1912},[1736,29915,891],{"class":1935},[1736,29917,29769],{"class":1912},[1736,29919,16972],{"class":1935},[1736,29921,29093],{"class":1912},[1736,29923,891],{"class":1935},[1736,29925,29778],{"class":1912},[1736,29927,16103],{"class":1935},[1736,29929,1939],{"class":1912},[1736,29931,29932,29935],{"class":1738,"line":1755},[1736,29933,29934],{"class":4866},"    ...",[1736,29936,29937],{"class":1912},"person\n",[1736,29939,29940],{"class":1738,"line":1761},[1736,29941,1971],{"class":1912},[1736,29943,29944],{"class":1738,"line":1767},[1736,29945,10582],{"class":1912},[11,29947,29948,29949,891],{},"And that's it. We now have a new array of names with a fullName plus the first and last names. We can add more keys to the objects in our people array and we will receive these new keys without having to modify anything thanks to the use of the spread operator. And of course we could in fact add more than one item to the names array if we wanted to. Have fun with ",[179,29950,29416],{},[11,29952,29953,29954,28818],{},"Feel free to play around with the ",[15,29955,28817],{"href":29956,"rel":29957},"https://codepen.io/debs-obrien/pen/YzqXVgd",[19],[23,29959,29960],{"id":28827},"Learn more",[70,29962,29963,29968,29975,29979],{},[73,29964,29359,29965,29365],{},[15,29966,29364],{"href":29362,"rel":29967},[19],[73,29969,28831,29970],{},[15,29971,29974],{"href":29972,"rel":29973},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map",[19],"Array.prototype.map()",[73,29976,28841,29977],{},[15,29978,28845],{"href":28844},[73,29980,28841,29981],{},[15,29982,28851],{"href":28850},[2011,29984,29985],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":307,"searchDepth":748,"depth":748,"links":29987},[29988],{"id":28827,"depth":748,"text":29960},"2020-08-09","The .map() method in JavaScript lets you loop over every element in an array and modify or add to it and then return a different element to take that elements place","photo-1512418490979-92798cec1380?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},{"title":29408,"description":29990},"blog/js-array-map-method",[5637],"btgRZtqh95nfQxhjK772p1yWAZO0o7rwgivB_BLpUwc",{"id":29998,"title":29999,"body":30000,"canonical":788,"date":30040,"description":30041,"extension":786,"featured":787,"image":788,"meta":30042,"navigation":790,"ogimage":788,"path":30043,"provider":788,"published":787,"seo":30044,"stem":30045,"tags":30046,"url":788,"__hash__":30047},"blog/blog/laid-off-what-now.md","Laid off from my dream job, what now?",{"type":8,"value":30001,"toc":30038},[30002,30005,30008,30011,30014,30017,30020,30023,30026,30029,30032,30035],[11,30003,30004],{},"It’s time to say goodbye. Unfortunately I have been the latest victim of layoffs due to reorgs in Microsoft Spain. I have taken some time to process but I won’t lie, It has been really hard to deal with. This wasn’t just a job. This was my dream. And my dream came true. It was a 5 year plan that I managed to achieve in 4. I got to work with some of the best people in the industry and I learnt a ton. I was pushed to my limits and grew in so many ways.",[11,30006,30007],{},"I led the Playwright community to what it is today and created the ambassador program. Created many videos and learning content such as workshops and blog posts, restructured the docs and made them more beginner friendly and spoke at many conferences both in person and online as well as podcasts, and live streams. Grew the Playwright LinkedIn community to over 44k and Discord to over 18k and YouTube to 27k and created videos that reached over 100k views and the Playwright dev.to with blog posts of over 269k views. And probably so much more that I just can’t think of right now.",[11,30009,30010],{},"I worked closely with the Playwright engineers to not just understand everything about Playwright but to also bring back the ideas from the community to the engineers. This led to the creating of UI mode which is still one of my favourite features of Playwright. I also drove the idea for the Playwright MCP and Playwright Agents by constantly experimenting with our tools and pushing my crazy ideas forward. I won’t lie, it wasn’t always easy and I didn’t always win but when an idea doesn’t make it forward it just pushes you to think harder and experiment more.",[11,30012,30013],{},"In less than 4 years of working here I managed to get promoted to Principal which is not an easy promotion at all and all this while navigating pregnancy, maternity leave and raising twins with no daycare. How on earth I did it I sometimes do not know but I had a great team and great managers and they helped guide me and constantly push me to deliver content not just quantity but high quality, and they encouraged me to experiment and continue to learn and grow and dive into AI in every shape and form. Listening and learning from others, building and bringing forward ideas, (and continuing to improve on those ideas if they weren’t accepted) and contributing to the success of others are the keys to my success.",[11,30015,30016],{},"I am super grateful to be able to say that I worked for Microsoft and it is by far the best company I have ever worked for in many ways. Layoffs suck as they are not personal and do not take into account how you performed. You end up just being in the wrong column in an excel sheet that got deleted and there is nothing noone can do about it. And I think that makes it harder to deal with cause there is nothing you can do to fix it or to change it. There is no one to blame, no-one at fault. So you just become one of the unlucky ones and you have to leave the job and team and company you love.",[11,30018,30019],{},"So how am I dealing with it? Like every normal person, I have my up days and my down days. I had days where I couldn’t even go for a run and other days where I needed to run and think. I reached out to friends and my network of people especially when I was having those hard days and that support was so necessary. Not just someone to talk to but people reminding you of how good you are and the hard work you have done and that this isn’t personal. I had many chats with my colleagues and all my managers all the way up the chain who were also surprised and saddened by this news and were also extremely supportive in every way.",[11,30021,30022],{},"It is like dealing with grief and the only way through it is to talk about it and keep moving forward. Yes it’s easy to just lie on the floor and not get up or sit on the sofa and eat chocolate, of which I did both. But it doesn’t get you anywhere so I didn’t lie there for long and I didn’t eat too much chocolate. Instead I focused on what I love doing and so kept playing with my site and other sites and building and trying new things and also taking time to just go running or sit under a tree and just think and process and try to accept this new change being forced upon me. I am very privileged in that I was not financially worried and also not worried about not getting another job. My biggest worry was leaving where I am to go to somewhere and it not being the same as in team collab, ways of working, etc. I never expected to ever have to find another job again as I never had any intentions of leaving Microsoft so I was in no way prepared for this at all.",[11,30024,30025],{},"But after weeks of processing and taking time away too I came to terms with it and managed to actually tell people. It’s only when you start telling people that it does actually feel real and then you cry but then you start to sleep better and you realise you probably should have told people a lot sooner as telling people really is just you telling yourself that this is reality and that you need to move on. And change is not something us humans are good at. Change is hard especially when it is not our choice or we haven’t planned for it. But with each change, with each new door we open, we take steps in a different direction and we learn and grow in a different way. So it is my turn to start taking steps in different directions. I am not too sure which direction that will be. There are many paths and I just have to choose the right one. But it’s hard cause you can’t see what’s at the end of each path or how windy the path is.",[11,30027,30028],{},"But if there is one thing I have learnt from this experience is, at the end of the day it doesn’t really matter cause change is always available if things don’t work out and new paths are always there should you decide to look for them.",[11,30030,30031],{},"If anyone asks me if they should consider taking a job at Microsoft my answer would be “hell yes”. Microsoft is by far the best company I have worked and the most talented people I have worked with. The company culture is like no other I have experienced and how everyone pushes each other to be successful rather than trying to compete or achieve things alone. Team work is essential and so I have been lucky to collaborate with so many talented people and have had such amazing experiences that I will never forget. Working for Microsoft is not easy, it is fast paced, at least it was on my team, and there is so much to navigate and learn and so many ideas that you can bring forward and everyone is willing to listen and let you try them out and support you in whatever you need in order to make it happen. You are never bored, never done and always have many mountains to climb. It is challenging in every way and very rewarding as you do meaningful work that gets seen by so many. Layoffs suck, and layoffs will continue to happen over the years in our field of work. So I am grateful I had the opportunity to work here and recommend it in a heartbeat. But the majority of roles now require you to go back to office so do keep that in mind. A few years ago I would have totally moved to Seattle or San Francisco, the offices are amazing and I have visited both and love going there but right now with 2 small kids it is just not feasible for me to move to the US.",[11,30033,30034],{},"Therefore it is time for me to move on and find a new role in a company that allows you to work remote as spending time with family instead of commuting is important to me and I live on a small island so offices here are pretty limited. I have already started interviewing and hopefully I will have something lined up soon. Not because I need it financially but because I am not good at sitting around doing nothing and because right now we are at the most exciting time in AI advancements and I have never been more exited about working in tech. I got my first job in tech in 1999 but the web was so boring then that I left it to go work on a stage and have fun rather than doing repetitive tasks. Tasks that now AI is doing for us making our jobs much more fun and creative. So I really don’t want to take time off, I want to help shape the way we work and share my passion and thoughts and learnings so others can improve their workflow and automate their day to day tasks so they can get time back to make their jobs more meaningful and do the things they love, such as more sport, or spend time with family.",[11,30036,30037],{},"Thanks Microsoft and everyone I have worked with. It has been a blast. And I am super grateful for the time we spent together and for the Playwright community for being so supportive and awesome.",{"title":307,"searchDepth":748,"depth":748,"links":30039},[],"2025-12-10","It’s time to say goodbye. Unfortunately I have been the latest victim of layoffs due to reorgs in Microsoft Spain. I have taken some time to process but I won’t lie, It has been really hard to deal with. This wasn’t just a job. This was my dream. And my dream came true. But unfortunately all dreams must come to an end at some point. So what now?",{},"/blog/laid-off-what-now",{"title":29999,"description":30041},"blog/laid-off-what-now",[3464],"_-ahTUqSOpx_4sy1c58y5VG0-pzY7cozOdyT8fpoa6Y",{"id":30049,"title":30050,"body":30051,"canonical":788,"date":30328,"description":30329,"extension":786,"featured":787,"image":30330,"meta":30331,"navigation":790,"ogimage":788,"path":30333,"provider":5235,"published":787,"seo":30334,"stem":30335,"tags":30336,"url":788,"__hash__":30337},"blog/blog/learning-about-developer-advocates.md","Learning about Developer Advocates",{"type":8,"value":30052,"toc":30316},[30053,30056,30059,30064,30068,30071,30074,30077,30081,30084,30087,30092,30096,30099,30102,30105,30108,30113,30117,30120,30124,30127,30131,30145,30159,30172,30186,30190,30219,30224,30233,30236,30249,30252,30261,30264,30268,30282,30303,30305,30308,30311],[11,30054,30055],{},"Being a Developer Advocate is by far the best job in the world, at least in my opinion and for me. It isn't for everyone and that's ok too. There have been many posts written about what is a Developer Advocate and many twitter spaces on how to get into DevRel so I won't go into that in this post but link and embed some great resources at the end of the post that will help you if you are looking to get into DevRel or have just started your role as a Developer Advocate and don't know where to start.",[11,30057,30058],{},"In this post I am going to share a bit about what I do as a Developer Advocate and what I love about it but it doesn't mean that you have to have all these skills to be a Developer Advocate because",[1713,30060,30061],{},[11,30062,30063],{},"everyone is different and everyone brings different skills to the table.",[23,30065,30067],{"id":30066},"how-i-got-started","How I got Started",[11,30069,30070],{},"First of all let me explain how I got started as a Developer Advocate. It all started with my love for speaking. I do like to talk. When I went to my first conference I knew from that moment that I wanted to be a speaker but I was terrified. Of the stage? Not at all. I had been an actress for many years involved in film, tv, radio and theatre. Yes I even got to be in a film with Jared Leto. That is about as far as my claim to fame goes I am afraid. My fear was that how could I stand up on a stage and give a talk to a room full of experts. That is what we call Imposter Syndrome and that could be a whole post in itself.",[11,30072,30073],{},"I was lucky to work with a great team who believed in me and pushed me forward to apply for conferences and so I did and I gave my first talk at a conference in Lithuania. I was super nervous but once I was in front of people I just felt at home. But what I really gained from this experience was a network to other speakers, to amazing people who thought I was amazing and that energy is always what drives me forward. And so I kept applying to conferences and mixing with speakers. And that's when I started looking for a job as a Developer Advocate.",[11,30075,30076],{},"But was I good enough? Did I have the right skills? In my mind, of course not. But again, luckily I had my amazing network of speakers who always encouraged me to push myself and so I did and actually it was at a conference that I was offered a job as a Developer Advocate.",[23,30078,30080],{"id":30079},"you-got-the-role-what-next","You got the Role. What Next?",[11,30082,30083],{},"Once you land the role as a Developer Advocate then what do you do? There is no course, no workshop and no real guidance to tell you what to do. Of course the company you work for will have ideas of what they want from you. For example my first role included docs, blogging, talks, videos, newsletters and in general helping and growing the community which in my opinion is the main goal of a Developer Advocate, at least for the roles I have worked in.",[11,30085,30086],{},"But of course it is hard to understand if you are doing a good job, if your talk has made impact, if your videos are worth all the time you invested in them. It's not easy to measure your work especially over a short space of time. Videos can take months or even years to get massive views and that should be ok with your company. Unless you already have a channel of 100k followers your video is probably not going to get that many visits right away.",[1713,30088,30089],{},[11,30090,30091],{},"Sometimes KPIs just don't work when it comes to DevRel roles.",[23,30093,30095],{"id":30094},"typical-day-as-a-developer-advocate","Typical Day as a Developer Advocate",[11,30097,30098],{},"So what does my typical day look like. First of all I work way to much and that's cause I love my job and I have no idea how to differenciate between what is me working and what is me studying to improve my skills or building something for fun especially when you work for a product that you love. However I do try to have a good work life balance as best I can.",[11,30100,30101],{},"I normally get up at 7am and go straight to the office, the room next door to my bedroom. I try to use my first few hours to learn something new, do a course or a workshop unless of course I have something urgent that I need to attend to. I then go to the gym or for a run or cycle. I love sport and it energizes me and sets me up for the day. When I get back I start checking slack messages. OK here is where I tell a lie. I always check my slack messages as they come through on my watch so even out running I normally know if something is happening at the office and if it is urgent enough to stop my run for. Really should stop doing that. After checking slack messages I either answer them or chat with who I need to chat with or just go about what I need to do for the day.",[11,30103,30104],{},"What do I do in a day? Great question. It varies a lot depending on if there is a new feature of the product that I need to learn, implement, test out etc. Or perhaps I need to create a demo for it to see it working and to show others how it works. From that demo it is really easy to create a blog post or a video. Sometimes I might have to create a talk for a conference or a workshop.",[11,30106,30107],{},"My role is very much mixed with creating content and working on or with the product in some sort of technical way and that is really important to me as I feel I couldn't do my job properly and advocate for it if I wasn't working with it. Another thing I love to do is to help improve the product by improving the developer experience. And of course Twitter. Twitter is an important part of my day as it is how I keep up with what is going on in the developer world and how I connect with other developers.",[1713,30109,30110],{},[11,30111,30112],{},"There is always something to do, always something to learn and the best part of my job is reaching out to the community and helping them as much as possible.",[23,30114,30116],{"id":30115},"working-as-the-only-developer-advocate-in-the-company","Working as the only Developer Advocate in the Company",[11,30118,30119],{},"Over the past few years I have been really lucky to have a great network of people in the DevRel scene. I haven't had the opportunity to have a team or be part of a DevRel team and that for me has been the hardest part of my job. But with every problem comes a solution so what I did was reach out to some of my friends who were in the DevRel scene and we set up weekly/biweekly calls which I have been having for almost 2 years now and this has really helped as I can talk openly to other like minded people about how to best do my job and sometimes even just to get an opinion on things I am doing or creating. With other speaker friends we have kept in contact through Twitter DM's and catch up calls and really I don't know what I would have done without them as they have helped make me be a better developer advocate.",[23,30121,30123],{"id":30122},"want-to-get-started-in-devrel","Want to get Started in DevRel?",[11,30125,30126],{},"If you are looking to get started in DevRel then don't make the mistake of thinking you are not good enough cause you most likely are. You don't have to be a good speaker to be a good Developer Advocate. I happen to have many years of stage and camera experience so things like that are not a problem for me. But did you know that some Developer Advocates never speak at conferences or create videos? That's because everyone brings a different skill to the table and perhaps you are good in other things like creating content, demos, blogs or even teaching workshops.",[138,30128,30130],{"id":30129},"reading-resources","Reading Resources",[11,30132,30133,30134,34,30139,30144],{},"I highly recommend reading this book: ",[15,30135,30138],{"href":30136,"rel":30137},"https://learn.samjulien.com/getting-started-in-developer-relations",[19],"Getting Started in Developer Relations",[15,30140,30143],{"href":30141,"rel":30142},"https://twitter.com/samjulien",[19],"Sam Julien"," who is director fof DevRel for Auth0.",[11,30146,30147,30148,30153,30154,891],{},"However there are other free resources if you can't afford the book. The amazing ",[15,30149,30152],{"href":30150,"rel":30151},"https://twitter.com/techgirl1908",[19],"Angie Jones"," who is very well known in the DevRel scene has a great post on ",[15,30155,30158],{"href":30156,"rel":30157},"https://github.com/readme/guides/angie-jones-demystifying-developer-advocacy",[19],"Demystifying Developer Advocacy",[11,30160,30161,30166,30167],{},[15,30162,30165],{"href":30163,"rel":30164},"https://twitter.com/manekinekko",[19],"Wassim Chegham",", another amazing Developer Advocate who works at Microsoft has created this post along with a great diagram explaining ",[15,30168,30171],{"href":30169,"rel":30170},"https://dev.to/wassimchegham/the-subtle-art-of-being-a-developer-advocate-gdg",[19],"The Subtle Art of Being a Developer Advocate",[11,30173,30174,30175,30180,30181],{},"If you are interested in career ladders for Dev Advocates then ",[15,30176,30179],{"href":30177,"rel":30178},"https://twitter.com/sarah_edo",[19],"Sarah Drasner"," has created this great resource: ",[15,30182,30185],{"href":30183,"rel":30184},"https://career-ladders.dev/devex",[19],"Career Ladders DX",[138,30187,30189],{"id":30188},"video-resoucres","Video Resoucres",[11,30191,30192,30193,829,30196,829,30199,829,30204,829,30209,608,30214],{},"There have been some great twitter spaces lately on Dev Advocacy and here are some of my favorite people in the DevRel scene including ",[15,30194,30152],{"href":30150,"rel":30195},[19],[15,30197,30179],{"href":30177,"rel":30198},[19],[15,30200,30203],{"href":30201,"rel":30202},"https://twitter.com/colbyfayock",[19],"Colby Fayock",[15,30205,30208],{"href":30206,"rel":30207},"https://twitter.com/kelseyhightower",[19],"Kelsey Hightower",[15,30210,30213],{"href":30211,"rel":30212},"https://twitter.com/editingemily",[19],"Emily Freeman",[15,30215,30218],{"href":30216,"rel":30217},"https://twitter.com/jamesqquick",[19],"James Q Quick",[5932,30220],{"width":5934,"height":5935,"src":30221,"title":30222,"frameBorder":1290,"allow":30223,"allowFullScreen":790},"https://www.youtube.com/embed/_q_bWATVJTg","YouTube video player","accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",[11,30225,30226,30227,30232],{},"One of the people I look up to the most in the DevRel scene is ",[15,30228,30231],{"href":30229,"rel":30230},"https://twitter.com/martinwoodward",[19],"Martin Woodward"," Senior Director of DevRel at GitHub who takes us through the journey of DevRel at GitHub and how they grew the team.",[5932,30234],{"width":5934,"height":5935,"src":30235,"title":30222,"frameBorder":1290,"allow":30223,"allowFullScreen":790},"https://www.youtube.com/embed/neB2aleEJ4c",[11,30237,30238,30239,608,30244,891],{},"Promoting myself here but checkout our DevRel Round table discussion with myself, ",[15,30240,30243],{"href":30241,"rel":30242},"https://twitter.com/timbenniks",[19],"Tim Benniks",[15,30245,30248],{"href":30246,"rel":30247},"https://twitter.com/li_hbr",[19],"Lucie Haberer",[5932,30250],{"width":5934,"height":5935,"src":30251,"title":30222,"frameBorder":1290,"allow":30223,"allowFullScreen":790},"https://www.youtube.com/embed/lSxU_q-8Rrc",[11,30253,30254,30255,30260],{},"Also I have another interview with ",[15,30256,30259],{"href":30257,"rel":30258},"https://twitter.com/themarcba",[19],"Marc Backes",", who has now become a Developer Advocate since hosting this interview.",[5932,30262],{"width":5934,"height":5935,"src":30263,"title":30222,"frameBorder":1290,"allow":30223,"allowFullScreen":790},"https://www.youtube.com/embed/WlpHiYQGG6k",[138,30265,30267],{"id":30266},"audio-resources","Audio Resources",[11,30269,30270,30271,28135,30276,30281],{},"Another Great Twitter Space is ",[15,30272,30275],{"href":30273,"rel":30274},"https://twitter.com/blackgirlbytes/status/1480656167487479808?s=20&t=VTTINGBZ1Ux20T_snpclBg",[19],"How to Lead DevRel Teams",[15,30277,30280],{"href":30278,"rel":30279},"https://twitter.com/Devocate_/status/1489611546707824656?s=20&t=8e7S0tcKeQgCSVSrHsP3LA",[19],"DevRel Hiring Manager AMA"," which both share some great tips and advice and are well worth listening to.",[11,30283,30284,30285,30288,30289,608,30294,30297,30298,891],{},"I have also been interviewed on a few podcasts about DevRel, ",[15,30286,3966],{"href":3964,"rel":30287},[19]," with ",[15,30290,30293],{"href":30291,"rel":30292},"https://twitter.com/margo_hdb",[19],"Margo McCabe",[15,30295,4017],{"href":4015,"rel":30296},[19]," on ",[15,30299,30302],{"href":30300,"rel":30301},"https://twitter.com/EnjoyTheVueCast",[19],"Enjoy the Vue",[23,30304,3294],{"id":3293},[11,30306,30307],{},"The DevRel scene is a great scene to be in and there are a lot of interesting roles out there. Give it a go and let others decide if you are a good fit or not and if you get rejected then just try another company as roles and skills vary a lot depending on the company and what they are looking for. As this role is pretty new it is hard to find people with a lot of experience so don't worry about not having experience. Although I would recommend having some sort of experience in creating content, communities, mentoring, etc. Be involved and active in the community as much as you can and don't be reach out to people and make new connections especially if you have already started in the DevRel scene. We are all just one big happy family really so welcome to the family.",[11,30309,30310],{},"I hope this post and the links helped at all. If they do let me know on Twitter and of course follow as many DevRels as you can and learn from them. There is no expert out there, we are all just learning and for sure you will be able to teach us a thing or two in the not so distant future.",[1713,30312,30313],{},[11,30314,30315],{},"Good Luck, You got this!",{"title":307,"searchDepth":748,"depth":748,"links":30317},[30318,30319,30320,30321,30322,30327],{"id":30066,"depth":748,"text":30067},{"id":30079,"depth":748,"text":30080},{"id":30094,"depth":748,"text":30095},{"id":30115,"depth":748,"text":30116},{"id":30122,"depth":748,"text":30123,"children":30323},[30324,30325,30326],{"id":30129,"depth":756,"text":30130},{"id":30188,"depth":756,"text":30189},{"id":30266,"depth":756,"text":30267},{"id":3293,"depth":748,"text":3294},"2022-02-06","Being a Developer Advocate is by far the best job in the world, at least in my opinion and for me. It isn't for everyone and that's ok too. In this post I am going to share a bit about what I do as a Developer Advocate and what I love about it.","photo-1560155016-bd4879ae8f21?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1364&q=80",{"ogImage":30332},"https://images.unsplash.com/photo-1560155016-bd4879ae8f21?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1364&q=80?fm=webp&fit=crop&q=80&w=480","/blog/learning-about-developer-advocates",{"title":30050,"description":30329},"blog/learning-about-developer-advocates",[3464,5531],"PfnkDwoTJz8rdBWR9DRRRsu4qEFXPHK_SWws6oPfCqs",{"id":30339,"title":30340,"body":30341,"canonical":788,"date":31965,"description":30345,"extension":786,"featured":787,"image":31966,"meta":31967,"navigation":790,"ogimage":788,"path":31968,"provider":5235,"published":787,"seo":31969,"stem":31970,"tags":31971,"url":788,"__hash__":31972},"blog/blog/learning-react.md","Learning React",{"type":8,"value":30342,"toc":31947},[30343,30346,30350,30361,30390,30393,30424,30427,30465,30469,30472,30498,30502,30505,30562,30566,30569,30643,30647,30657,30720,30724,30742,30767,30771,30785,30824,30827,30892,30896,30907,31011,31014,31217,31226,31241,31255,31259,31262,31266,31299,31315,31319,31353,31385,31388,31392,31402,31409,31445,31448,31501,31506,31542,31548,31664,31671,31738,31742,31745,31788,31791,31849,31852,31908,31910,31913,31916,31919,31944],[11,30344,30345],{},"Recently I started learning React for my new job. I basically went from being a coding ninja to being a coding newbie. But hey everything can be learnt so here is what I have learnt so far.",[23,30347,30349],{"id":30348},"entering-javascript-land","Entering JavaScript Land",[11,30351,30352,30353,30356,30357,30360],{},"Use ",[179,30354,30355],{},"{}","to enter JavaScript land. In Vue we use double these so I just need to remember that from now on it's ony one and if I see double ",[179,30358,30359],{},"{{}}"," it means JavaScript land followed by an object.",[299,30362,30364],{"className":4894,"code":30363,"language":4896,"meta":307,"style":307},"\u003Ch2 style={{ color: 'black' }}>hi\u003C/h2>\n",[179,30365,30366],{"__ignoreMap":307},[1736,30367,30368,30370,30372,30375,30377,30380,30383,30386,30388],{"class":1738,"line":1739},[1736,30369,6657],{"class":1912},[1736,30371,23],{"class":6696},[1736,30373,30374],{"class":2674}," style",[1736,30376,5062],{"class":4866},[1736,30378,30379],{"class":1912},"{{ color: ",[1736,30381,30382],{"class":1935},"'black'",[1736,30384,30385],{"class":1912}," }}>hi\u003C/",[1736,30387,23],{"class":6696},[1736,30389,6663],{"class":1912},[11,30391,30392],{},"This is easier to understand when there is more than one value in our object.",[299,30394,30396],{"className":4894,"code":30395,"language":4896,"meta":307,"style":307},"\u003Ch2 style={{ color: 'black', padding: '10px' }}>hi\u003C/h2>\n",[179,30397,30398],{"__ignoreMap":307},[1736,30399,30400,30402,30404,30406,30408,30410,30412,30415,30418,30420,30422],{"class":1738,"line":1739},[1736,30401,6657],{"class":1912},[1736,30403,23],{"class":6696},[1736,30405,30374],{"class":2674},[1736,30407,5062],{"class":4866},[1736,30409,30379],{"class":1912},[1736,30411,30382],{"class":1935},[1736,30413,30414],{"class":1912},", padding: ",[1736,30416,30417],{"class":1935},"'10px'",[1736,30419,30385],{"class":1912},[1736,30421,23],{"class":6696},[1736,30423,6663],{"class":1912},[11,30425,30426],{},"React components are just functions that return something. Normally some html.",[299,30428,30430],{"className":4894,"code":30429,"language":4896,"meta":307,"style":307},"export default function Button() {\n  return \u003Cbutton>click me\u003C/button>\n}\n",[179,30431,30432,30446,30461],{"__ignoreMap":307},[1736,30433,30434,30436,30439,30441,30444],{"class":1738,"line":1739},[1736,30435,6632],{"class":4866},[1736,30437,30438],{"class":4866}," default",[1736,30440,6674],{"class":4866},[1736,30442,30443],{"class":2674}," Button",[1736,30445,6680],{"class":1912},[1736,30447,30448,30450,30452,30454,30457,30459],{"class":1738,"line":748},[1736,30449,6685],{"class":4866},[1736,30451,10193],{"class":1912},[1736,30453,14849],{"class":6696},[1736,30455,30456],{"class":1912},">click me\u003C/",[1736,30458,14849],{"class":6696},[1736,30460,6663],{"class":1912},[1736,30462,30463],{"class":1738,"line":756},[1736,30464,1976],{"class":1912},[138,30466,30468],{"id":30467},"functions-with-capital-letters","Functions with Capital Letters",[11,30470,30471],{},"These functions need to be named with a capital letter. Which makes sense as then it is easy for the browser to distinguish between what is html and what is a React component.",[299,30473,30475],{"className":21974,"code":30474,"language":21976,"meta":307,"style":307},"\u003Cbutton> // html \u003Cbutton /> // React Component\u003C/button>\n",[179,30476,30477],{"__ignoreMap":307},[1736,30478,30479,30481,30483,30486,30488,30491,30494,30496],{"class":1738,"line":1739},[1736,30480,6657],{"class":1912},[1736,30482,14849],{"class":6696},[1736,30484,30485],{"class":1912},"> // html \u003C",[1736,30487,14849],{"class":6696},[1736,30489,13133],{"class":30490},"s7hpK",[1736,30492,30493],{"class":1912},"> // React Component\u003C/",[1736,30495,14849],{"class":6696},[1736,30497,6663],{"class":1912},[23,30499,30501],{"id":30500},"using-jsx","Using JSX",[11,30503,30504],{},"React components use JSX which basically means we can add dynamic stuff to our html. And of course you can pass props into the component",[299,30506,30508],{"className":4894,"code":30507,"language":4896,"meta":307,"style":307},"const name = 'Debbie'\n\nexport default function Name(name) {\n  return \u003Ch1> Hi {name}\u003C/h1>\n}\n",[179,30509,30510,30521,30525,30542,30558],{"__ignoreMap":307},[1736,30511,30512,30514,30517,30519],{"class":1738,"line":1739},[1736,30513,5029],{"class":4866},[1736,30515,30516],{"class":1918}," name",[1736,30518,4911],{"class":4866},[1736,30520,29715],{"class":1935},[1736,30522,30523],{"class":1738,"line":748},[1736,30524,1747],{"emptyLinePlaceholder":790},[1736,30526,30527,30529,30531,30533,30536,30538,30540],{"class":1738,"line":756},[1736,30528,6632],{"class":4866},[1736,30530,30438],{"class":4866},[1736,30532,6674],{"class":4866},[1736,30534,30535],{"class":2674}," Name",[1736,30537,7751],{"class":1912},[1736,30539,15955],{"class":5036},[1736,30541,7246],{"class":1912},[1736,30543,30544,30546,30548,30551,30554,30556],{"class":1738,"line":1755},[1736,30545,6685],{"class":4866},[1736,30547,10193],{"class":1912},[1736,30549,30550],{"class":6696},"h1",[1736,30552,30553],{"class":1912},"> Hi {name}\u003C/",[1736,30555,30550],{"class":6696},[1736,30557,6663],{"class":1912},[1736,30559,30560],{"class":1738,"line":1761},[1736,30561,1976],{"class":1912},[23,30563,30565],{"id":30564},"return-statements","Return Statements",[11,30567,30568],{},"One thing to watch out for is the return statement. We can return things on one line but if we have more things to return and need more than one line then we must use brackets. And important to remember is that we cannot use a semicolon at the end of the line inside these brackets. If we do it will break.",[299,30570,30572],{"className":4894,"code":30571,"language":4896,"meta":307,"style":307},"export default function Hi() {\n  return (\n    \u003Cdiv>\n      \u003Ch1>Hi\u003C/h1>\n      \u003Cp>welcome to our world\u003C/p>\n    \u003C/div>\n  )\n}\n",[179,30573,30574,30587,30593,30601,30614,30627,30635,30639],{"__ignoreMap":307},[1736,30575,30576,30578,30580,30582,30585],{"class":1738,"line":1739},[1736,30577,6632],{"class":4866},[1736,30579,30438],{"class":4866},[1736,30581,6674],{"class":4866},[1736,30583,30584],{"class":2674}," Hi",[1736,30586,6680],{"class":1912},[1736,30588,30589,30591],{"class":1738,"line":748},[1736,30590,6685],{"class":4866},[1736,30592,6688],{"class":1912},[1736,30594,30595,30597,30599],{"class":1738,"line":756},[1736,30596,6693],{"class":1912},[1736,30598,6697],{"class":6696},[1736,30600,6663],{"class":1912},[1736,30602,30603,30605,30607,30610,30612],{"class":1738,"line":1755},[1736,30604,6710],{"class":1912},[1736,30606,30550],{"class":6696},[1736,30608,30609],{"class":1912},">Hi\u003C/",[1736,30611,30550],{"class":6696},[1736,30613,6663],{"class":1912},[1736,30615,30616,30618,30620,30623,30625],{"class":1738,"line":1761},[1736,30617,6710],{"class":1912},[1736,30619,11],{"class":6696},[1736,30621,30622],{"class":1912},">welcome to our world\u003C/",[1736,30624,11],{"class":6696},[1736,30626,6663],{"class":1912},[1736,30628,30629,30631,30633],{"class":1738,"line":1767},[1736,30630,6744],{"class":1912},[1736,30632,6697],{"class":6696},[1736,30634,6663],{"class":1912},[1736,30636,30637],{"class":1738,"line":1772},[1736,30638,6753],{"class":1912},[1736,30640,30641],{"class":1738,"line":1778},[1736,30642,1976],{"class":1912},[23,30644,30646],{"id":30645},"a-single-root-element","A Single Root element",[11,30648,30649,30650,30653,30654,30656],{},"Just like in Vue 2 you can't render multiple items such as the example above without wrapping it in the ",[179,30651,30652],{},"\u003Cdiv>",". However React gives you an element called a fragment so that you don't have to render a ",[179,30655,30652],{},". A fragment is just like empty syntax and won't get rendered.",[299,30658,30660],{"className":4894,"code":30659,"language":4896,"meta":307,"style":307},"export default function Hi() {\n  return (\n    \u003C>\n      \u003Ch1>Hi\u003C/h1>\n      \u003Cp>welcome to our world\u003C/p>\n    \u003C/>\n  )\n}\n",[179,30661,30662,30674,30680,30684,30696,30708,30712,30716],{"__ignoreMap":307},[1736,30663,30664,30666,30668,30670,30672],{"class":1738,"line":1739},[1736,30665,6632],{"class":4866},[1736,30667,30438],{"class":4866},[1736,30669,6674],{"class":4866},[1736,30671,30584],{"class":2674},[1736,30673,6680],{"class":1912},[1736,30675,30676,30678],{"class":1738,"line":748},[1736,30677,6685],{"class":4866},[1736,30679,6688],{"class":1912},[1736,30681,30682],{"class":1738,"line":756},[1736,30683,10262],{"class":1912},[1736,30685,30686,30688,30690,30692,30694],{"class":1738,"line":1755},[1736,30687,6710],{"class":1912},[1736,30689,30550],{"class":6696},[1736,30691,30609],{"class":1912},[1736,30693,30550],{"class":6696},[1736,30695,6663],{"class":1912},[1736,30697,30698,30700,30702,30704,30706],{"class":1738,"line":1761},[1736,30699,6710],{"class":1912},[1736,30701,11],{"class":6696},[1736,30703,30622],{"class":1912},[1736,30705,11],{"class":6696},[1736,30707,6663],{"class":1912},[1736,30709,30710],{"class":1738,"line":1767},[1736,30711,10322],{"class":1912},[1736,30713,30714],{"class":1738,"line":1772},[1736,30715,6753],{"class":1912},[1736,30717,30718],{"class":1738,"line":1778},[1736,30719,1976],{"class":1912},[23,30721,30723],{"id":30722},"camelcase-for-jsx","CamelCase for JSX",[11,30725,30726,30727,30730,30731,30734,30735,30737,30738,30741],{},"With JSX we need to use camelCase and not kebab-case. That means we need to use ",[179,30728,30729],{},"backgroundColor"," instead of ",[179,30732,30733],{},"background-color",". Class is also a reserved name in JavaScript so we can't use it meaning we can't say use ",[179,30736,15630],{}," for our styling and have to use ",[179,30739,30740],{},"className","instead.",[299,30743,30745],{"className":4894,"code":30744,"language":4896,"meta":307,"style":307},"\u003Ch1 className=\"heading\">hi\u003C/h1>\n",[179,30746,30747],{"__ignoreMap":307},[1736,30748,30749,30751,30753,30755,30757,30760,30763,30765],{"class":1738,"line":1739},[1736,30750,6657],{"class":1912},[1736,30752,30550],{"class":6696},[1736,30754,6700],{"class":2674},[1736,30756,5062],{"class":4866},[1736,30758,30759],{"class":1935},"\"heading\"",[1736,30761,30762],{"class":1912},">hi\u003C/",[1736,30764,30550],{"class":6696},[1736,30766,6663],{"class":1912},[23,30768,30770],{"id":30769},"using-dynamic-values-in-jsx","Using Dynamic Values in JSX",[11,30772,30773,30774,30776,30777,30780,30781,30784],{},"In vue to add a dynamic value to an image we would bind the ",[179,30775,6723],{},"attribute by adding a colon before it ",[179,30778,30779],{},":src","but in React we don't bind the attribute but instead the value. We do this by removing the quotes and replacing them with curly brackets ",[179,30782,30783],{},"src={}",". If you think about it, it actually makes sense as using curly brackets means we are entering JavaScript land.",[299,30786,30788],{"className":4894,"code":30787,"language":4896,"meta":307,"style":307},"const image = \"https://placekitten.com/112\"\n\u003Cimg src={image} className=\"kitten\" />\n",[179,30789,30790,30802],{"__ignoreMap":307},[1736,30791,30792,30794,30797,30799],{"class":1738,"line":1739},[1736,30793,5029],{"class":4866},[1736,30795,30796],{"class":1918}," image",[1736,30798,4911],{"class":4866},[1736,30800,30801],{"class":1935}," \"https://placekitten.com/112\"\n",[1736,30803,30804,30806,30808,30810,30812,30815,30817,30819,30822],{"class":1738,"line":748},[1736,30805,6657],{"class":1912},[1736,30807,121],{"class":6696},[1736,30809,7026],{"class":2674},[1736,30811,5062],{"class":4866},[1736,30813,30814],{"class":1912},"{image} ",[1736,30816,30740],{"class":2674},[1736,30818,5062],{"class":4866},[1736,30820,30821],{"class":1935},"\"kitten\"",[1736,30823,6739],{"class":1912},[11,30825,30826],{},"As we are entering JavaScript land by using curly brackets it also means we can concatenate things.",[299,30828,30830],{"className":4894,"code":30829,"language":4896,"meta":307,"style":307},"const imgUrl = \"https://placekitten.com\"\n\u003Cimg src={imgURL + '/102'} />\n// OR\n\u003Cimg src={`${imgURL}/102`} />\n",[179,30831,30832,30844,30865,30870],{"__ignoreMap":307},[1736,30833,30834,30836,30839,30841],{"class":1738,"line":1739},[1736,30835,5029],{"class":4866},[1736,30837,30838],{"class":1918}," imgUrl",[1736,30840,4911],{"class":4866},[1736,30842,30843],{"class":1935}," \"https://placekitten.com\"\n",[1736,30845,30846,30848,30850,30852,30854,30857,30859,30862],{"class":1738,"line":748},[1736,30847,6657],{"class":1912},[1736,30849,121],{"class":6696},[1736,30851,7026],{"class":2674},[1736,30853,5062],{"class":4866},[1736,30855,30856],{"class":1912},"{imgURL ",[1736,30858,9343],{"class":4866},[1736,30860,30861],{"class":1935}," '/102'",[1736,30863,30864],{"class":1912},"} />\n",[1736,30866,30867],{"class":1738,"line":756},[1736,30868,30869],{"class":6820},"// OR\n",[1736,30871,30872,30874,30876,30878,30880,30882,30884,30887,30890],{"class":1738,"line":1755},[1736,30873,6657],{"class":1912},[1736,30875,121],{"class":6696},[1736,30877,7026],{"class":2674},[1736,30879,5062],{"class":4866},[1736,30881,7387],{"class":1912},[1736,30883,16967],{"class":1935},[1736,30885,30886],{"class":1912},"imgURL",[1736,30888,30889],{"class":1935},"}/102`",[1736,30891,30864],{"class":1912},[23,30893,30895],{"id":30894},"printing-out-lists","Printing out Lists",[11,30897,30898,30899,30902,30903,10141,30905,891],{},"Unlike in Vue there is no ",[179,30900,30901],{},"v-for","for rendering lists. We need to do this just like we would do in any JavaScript function by using ",[179,30904,29636],{},[179,30906,27263],{},[299,30908,30910],{"className":4894,"code":30909,"language":4896,"meta":307,"style":307},"const people = ['Debbie', 'Alex', 'Nat']\n\nexport default function List() {\n  const peopleList = people.map(person => \u003Cli key={person}>{person}\u003C/li>)\n\n  return \u003Cul>{peopleList}\u003C/ul>\n}\n",[179,30911,30912,30936,30940,30953,30988,30992,31007],{"__ignoreMap":307},[1736,30913,30914,30916,30918,30920,30922,30924,30926,30929,30931,30934],{"class":1738,"line":1739},[1736,30915,5029],{"class":4866},[1736,30917,28901],{"class":1918},[1736,30919,4911],{"class":4866},[1736,30921,8409],{"class":1912},[1736,30923,28914],{"class":1935},[1736,30925,829],{"class":1912},[1736,30927,30928],{"class":1935},"'Alex'",[1736,30930,829],{"class":1912},[1736,30932,30933],{"class":1935},"'Nat'",[1736,30935,8420],{"class":1912},[1736,30937,30938],{"class":1738,"line":748},[1736,30939,1747],{"emptyLinePlaceholder":790},[1736,30941,30942,30944,30946,30948,30951],{"class":1738,"line":756},[1736,30943,6632],{"class":4866},[1736,30945,30438],{"class":4866},[1736,30947,6674],{"class":4866},[1736,30949,30950],{"class":2674}," List",[1736,30952,6680],{"class":1912},[1736,30954,30955,30957,30960,30962,30964,30966,30968,30970,30972,30974,30976,30978,30980,30983,30985],{"class":1738,"line":1755},[1736,30956,7824],{"class":4866},[1736,30958,30959],{"class":1918}," peopleList",[1736,30961,4911],{"class":4866},[1736,30963,29305],{"class":1912},[1736,30965,14366],{"class":2674},[1736,30967,7751],{"class":1912},[1736,30969,29093],{"class":5036},[1736,30971,10208],{"class":4866},[1736,30973,10193],{"class":1912},[1736,30975,73],{"class":6696},[1736,30977,14761],{"class":2674},[1736,30979,5062],{"class":4866},[1736,30981,30982],{"class":1912},"{person}>{person}\u003C/",[1736,30984,73],{"class":6696},[1736,30986,30987],{"class":1912},">)\n",[1736,30989,30990],{"class":1738,"line":1761},[1736,30991,1747],{"emptyLinePlaceholder":790},[1736,30993,30994,30996,30998,31000,31003,31005],{"class":1738,"line":1767},[1736,30995,6685],{"class":4866},[1736,30997,10193],{"class":1912},[1736,30999,70],{"class":6696},[1736,31001,31002],{"class":1912},">{peopleList}\u003C/",[1736,31004,70],{"class":6696},[1736,31006,6663],{"class":1912},[1736,31008,31009],{"class":1738,"line":1772},[1736,31010,1976],{"class":1912},[11,31012,31013],{},"Filtering out Items. Say we wanted to filter out which people were working with which framework. We first need to filter over our array of people to get a new array of only those with 'React' in this case and then map over that new array of 'ReactPeople' to print out each person's name.",[299,31015,31017],{"className":4894,"code":31016,"language":4896,"meta":307,"style":307},"const people = [\n  {\n    name: 'Debbie',\n    framework: 'React'\n  },\n  {\n    name: 'Alex',\n    framework: 'Vue'\n  },\n  {\n    name: 'Nat',\n    framework: 'React'\n  }\n]\n\nconst reactPeople = people.filter(person => person.framework === 'React')\n\nexport default function List() {\n  const peopleList = reactPeople.map(person => (\n    \u003Cli key={person.name}>{person.name}\u003C/li>\n  ))\n\n  return \u003Cul>{peopleList}\u003C/ul>\n}\n",[179,31018,31019,31029,31033,31042,31050,31054,31058,31066,31073,31077,31081,31089,31095,31099,31103,31107,31136,31140,31152,31173,31190,31195,31199,31213],{"__ignoreMap":307},[1736,31020,31021,31023,31025,31027],{"class":1738,"line":1739},[1736,31022,5029],{"class":4866},[1736,31024,28901],{"class":1918},[1736,31026,4911],{"class":4866},[1736,31028,28906],{"class":1912},[1736,31030,31031],{"class":1738,"line":748},[1736,31032,17704],{"class":1912},[1736,31034,31035,31038,31040],{"class":1738,"line":756},[1736,31036,31037],{"class":1912},"    name: ",[1736,31039,28914],{"class":1935},[1736,31041,1939],{"class":1912},[1736,31043,31044,31047],{"class":1738,"line":1755},[1736,31045,31046],{"class":1912},"    framework: ",[1736,31048,31049],{"class":1935},"'React'\n",[1736,31051,31052],{"class":1738,"line":1761},[1736,31053,4929],{"class":1912},[1736,31055,31056],{"class":1738,"line":1767},[1736,31057,17704],{"class":1912},[1736,31059,31060,31062,31064],{"class":1738,"line":1772},[1736,31061,31037],{"class":1912},[1736,31063,30928],{"class":1935},[1736,31065,1939],{"class":1912},[1736,31067,31068,31070],{"class":1738,"line":1778},[1736,31069,31046],{"class":1912},[1736,31071,31072],{"class":1935},"'Vue'\n",[1736,31074,31075],{"class":1738,"line":1784},[1736,31076,4929],{"class":1912},[1736,31078,31079],{"class":1738,"line":1790},[1736,31080,17704],{"class":1912},[1736,31082,31083,31085,31087],{"class":1738,"line":1796},[1736,31084,31037],{"class":1912},[1736,31086,30933],{"class":1935},[1736,31088,1939],{"class":1912},[1736,31090,31091,31093],{"class":1738,"line":2353},[1736,31092,31046],{"class":1912},[1736,31094,31049],{"class":1935},[1736,31096,31097],{"class":1738,"line":2358},[1736,31098,1971],{"class":1912},[1736,31100,31101],{"class":1738,"line":2364},[1736,31102,8420],{"class":1912},[1736,31104,31105],{"class":1738,"line":2370},[1736,31106,1747],{"emptyLinePlaceholder":790},[1736,31108,31109,31111,31114,31116,31118,31120,31122,31124,31126,31129,31131,31134],{"class":1738,"line":2376},[1736,31110,5029],{"class":4866},[1736,31112,31113],{"class":1918}," reactPeople",[1736,31115,4911],{"class":4866},[1736,31117,29305],{"class":1912},[1736,31119,14507],{"class":2674},[1736,31121,7751],{"class":1912},[1736,31123,29093],{"class":5036},[1736,31125,10208],{"class":4866},[1736,31127,31128],{"class":1912}," person.framework ",[1736,31130,7786],{"class":4866},[1736,31132,31133],{"class":1935}," 'React'",[1736,31135,7045],{"class":1912},[1736,31137,31138],{"class":1738,"line":2381},[1736,31139,1747],{"emptyLinePlaceholder":790},[1736,31141,31142,31144,31146,31148,31150],{"class":1738,"line":2387},[1736,31143,6632],{"class":4866},[1736,31145,30438],{"class":4866},[1736,31147,6674],{"class":4866},[1736,31149,30950],{"class":2674},[1736,31151,6680],{"class":1912},[1736,31153,31154,31156,31158,31160,31163,31165,31167,31169,31171],{"class":1738,"line":2393},[1736,31155,7824],{"class":4866},[1736,31157,30959],{"class":1918},[1736,31159,4911],{"class":4866},[1736,31161,31162],{"class":1912}," reactPeople.",[1736,31164,14366],{"class":2674},[1736,31166,7751],{"class":1912},[1736,31168,29093],{"class":5036},[1736,31170,10208],{"class":4866},[1736,31172,6688],{"class":1912},[1736,31174,31175,31177,31179,31181,31183,31186,31188],{"class":1738,"line":2398},[1736,31176,6693],{"class":1912},[1736,31178,73],{"class":6696},[1736,31180,14761],{"class":2674},[1736,31182,5062],{"class":4866},[1736,31184,31185],{"class":1912},"{person.name}>{person.name}\u003C/",[1736,31187,73],{"class":6696},[1736,31189,6663],{"class":1912},[1736,31191,31192],{"class":1738,"line":2404},[1736,31193,31194],{"class":1912},"  ))\n",[1736,31196,31197],{"class":1738,"line":6959},[1736,31198,1747],{"emptyLinePlaceholder":790},[1736,31200,31201,31203,31205,31207,31209,31211],{"class":1738,"line":7296},[1736,31202,6685],{"class":4866},[1736,31204,10193],{"class":1912},[1736,31206,70],{"class":6696},[1736,31208,31002],{"class":1912},[1736,31210,70],{"class":6696},[1736,31212,6663],{"class":1912},[1736,31214,31215],{"class":1738,"line":7305},[1736,31216,1976],{"class":1912},[11,31218,31219,31220,31222,31223,31225],{},"Remember when working with arrow functions there is no return however if you add ",[179,31221,30355],{},"then you must add a return statement. Using ",[179,31224,30355],{},"also allows you to return more than just one line of code.",[11,31227,31228,31229,31232,31233,31236,31237,31240],{},"Just like in Vue adding a ",[179,31230,31231],{},"key","to lists is important. The only difference is that instead of ",[179,31234,31235],{},":key=\"name\"","in React it is ",[179,31238,31239],{},"key={name}",". Make sure to keep your keys unique. If you don't set a key React will use the index but if the position changes due to reordering then you will run into some issues.",[11,31242,31243,31244,31247,31248,31251,31252,31254],{},"If you need to pass in anything to a Fragment such as a key value then you need to actually write ",[179,31245,31246],{},"\u003CFragment key={name}>"," instead of just using ",[179,31249,31250],{},"\u003C>",". You can also use a ",[179,31253,30652],{}," if you prefer.",[23,31256,31258],{"id":31257},"exporting-components","Exporting Components",[11,31260,31261],{},"There are 2 ways to export a component. Using named exports or default exports. With default export you can only have one function to export but with named exports you can have more than one. The thing to remember is how you export it affects how you import it. With named exports you must use the same name when importing it. With default exports you can use any name you like.",[138,31263,31265],{"id":31264},"named-exports","Named Exports",[299,31267,31269],{"className":4894,"code":31268,"language":4896,"meta":307,"style":307},"export function Button() {\n  return \u003Cbutton>click me\u003C/button>\n}\n",[179,31270,31271,31281,31295],{"__ignoreMap":307},[1736,31272,31273,31275,31277,31279],{"class":1738,"line":1739},[1736,31274,6632],{"class":4866},[1736,31276,6674],{"class":4866},[1736,31278,30443],{"class":2674},[1736,31280,6680],{"class":1912},[1736,31282,31283,31285,31287,31289,31291,31293],{"class":1738,"line":748},[1736,31284,6685],{"class":4866},[1736,31286,10193],{"class":1912},[1736,31288,14849],{"class":6696},[1736,31290,30456],{"class":1912},[1736,31292,14849],{"class":6696},[1736,31294,6663],{"class":1912},[1736,31296,31297],{"class":1738,"line":756},[1736,31298,1976],{"class":1912},[299,31300,31302],{"className":4894,"code":31301,"language":4896,"meta":307,"style":307},"import { Button } from './button'\n",[179,31303,31304],{"__ignoreMap":307},[1736,31305,31306,31308,31310,31312],{"class":1738,"line":1739},[1736,31307,4996],{"class":4866},[1736,31309,8756],{"class":1912},[1736,31311,5002],{"class":4866},[1736,31313,31314],{"class":1935}," './button'\n",[138,31316,31318],{"id":31317},"default-exports","Default Exports",[299,31320,31321],{"className":4894,"code":30429,"language":4896,"meta":307,"style":307},[179,31322,31323,31335,31349],{"__ignoreMap":307},[1736,31324,31325,31327,31329,31331,31333],{"class":1738,"line":1739},[1736,31326,6632],{"class":4866},[1736,31328,30438],{"class":4866},[1736,31330,6674],{"class":4866},[1736,31332,30443],{"class":2674},[1736,31334,6680],{"class":1912},[1736,31336,31337,31339,31341,31343,31345,31347],{"class":1738,"line":748},[1736,31338,6685],{"class":4866},[1736,31340,10193],{"class":1912},[1736,31342,14849],{"class":6696},[1736,31344,30456],{"class":1912},[1736,31346,14849],{"class":6696},[1736,31348,6663],{"class":1912},[1736,31350,31351],{"class":1738,"line":756},[1736,31352,1976],{"class":1912},[299,31354,31356],{"className":4894,"code":31355,"language":4896,"meta":307,"style":307},"import Button from './button'\n// or\nimport MyButton from './button'\n",[179,31357,31358,31369,31374],{"__ignoreMap":307},[1736,31359,31360,31362,31365,31367],{"class":1738,"line":1739},[1736,31361,4996],{"class":4866},[1736,31363,31364],{"class":1912}," Button ",[1736,31366,5002],{"class":4866},[1736,31368,31314],{"class":1935},[1736,31370,31371],{"class":1738,"line":748},[1736,31372,31373],{"class":6820},"// or\n",[1736,31375,31376,31378,31381,31383],{"class":1738,"line":756},[1736,31377,4996],{"class":4866},[1736,31379,31380],{"class":1912}," MyButton ",[1736,31382,5002],{"class":4866},[1736,31384,31314],{"class":1935},[11,31386,31387],{},"You can mix having a default export and a named export in the one file but in general this can get confusing so it is best to only stick to either 2 named exports or 2 files with default exports.",[23,31389,31391],{"id":31390},"working-with-props","Working with Props",[11,31393,31394,31395,31397,31398,31401],{},"Props are used so parent components can pass information to child components. The props you can pass to html elements are predefined to conform with HTML standards, for example passing an ",[179,31396,6723],{}," prop to an ",[179,31399,31400],{},"\u003Cimg>","element. But you can pass any props you like to your own components.",[11,31403,31404,31405,31408],{},"When passing in props remember that ",[179,31406,31407],{},"({ color, height })"," is just using destructuring:",[299,31410,31412],{"className":4894,"code":31411,"language":4896,"meta":307,"style":307},"function MyImage({ color, height }) {\n  return //...\n}\n",[179,31413,31414,31434,31441],{"__ignoreMap":307},[1736,31415,31416,31418,31421,31423,31426,31428,31431],{"class":1738,"line":1739},[1736,31417,7745],{"class":4866},[1736,31419,31420],{"class":2674}," MyImage",[1736,31422,7233],{"class":1912},[1736,31424,31425],{"class":5036},"color",[1736,31427,829],{"class":1912},[1736,31429,31430],{"class":5036},"height",[1736,31432,31433],{"class":1912}," }) {\n",[1736,31435,31436,31438],{"class":1738,"line":748},[1736,31437,6685],{"class":4866},[1736,31439,31440],{"class":6820}," //...\n",[1736,31442,31443],{"class":1738,"line":756},[1736,31444,1976],{"class":1912},[11,31446,31447],{},"By destructuring you don't have to do something like this:",[299,31449,31451],{"className":4894,"code":31450,"language":4896,"meta":307,"style":307},"function MyImage(props) {\n  let color = props.color\n  let height = props.height\n  return //....\n}\n",[179,31452,31453,31466,31478,31490,31497],{"__ignoreMap":307},[1736,31454,31455,31457,31459,31461,31464],{"class":1738,"line":1739},[1736,31456,7745],{"class":4866},[1736,31458,31420],{"class":2674},[1736,31460,7751],{"class":1912},[1736,31462,31463],{"class":5036},"props",[1736,31465,7246],{"class":1912},[1736,31467,31468,31470,31473,31475],{"class":1738,"line":748},[1736,31469,16745],{"class":4866},[1736,31471,31472],{"class":1912}," color ",[1736,31474,5062],{"class":4866},[1736,31476,31477],{"class":1912}," props.color\n",[1736,31479,31480,31482,31485,31487],{"class":1738,"line":756},[1736,31481,16745],{"class":4866},[1736,31483,31484],{"class":1912}," height ",[1736,31486,5062],{"class":4866},[1736,31488,31489],{"class":1912}," props.height\n",[1736,31491,31492,31494],{"class":1738,"line":1755},[1736,31493,6685],{"class":4866},[1736,31495,31496],{"class":6820}," //....\n",[1736,31498,31499],{"class":1738,"line":1761},[1736,31500,1976],{"class":1912},[11,31502,31503,31504,891],{},"You can specify a default property for a prop using destructuring by adding an ",[179,31505,5062],{},[299,31507,31509],{"className":4894,"code":31508,"language":4896,"meta":307,"style":307},"function MyImage({ color, height = 50 }) {\n  return //...\n}\n",[179,31510,31511,31532,31538],{"__ignoreMap":307},[1736,31512,31513,31515,31517,31519,31521,31523,31525,31527,31530],{"class":1738,"line":1739},[1736,31514,7745],{"class":4866},[1736,31516,31420],{"class":2674},[1736,31518,7233],{"class":1912},[1736,31520,31425],{"class":5036},[1736,31522,829],{"class":1912},[1736,31524,31430],{"class":5036},[1736,31526,4911],{"class":4866},[1736,31528,31529],{"class":1918}," 50",[1736,31531,31433],{"class":1912},[1736,31533,31534,31536],{"class":1738,"line":748},[1736,31535,6685],{"class":4866},[1736,31537,31440],{"class":6820},[1736,31539,31540],{"class":1738,"line":756},[1736,31541,1976],{"class":1912},[11,31543,31544,31545],{},"Passing lots of props can get very repetitive and this is where the spread operator comes in ",[179,31546,31547],{},"{ ...props }",[299,31549,31551],{"className":4894,"code":31550,"language":4896,"meta":307,"style":307},"function Card({ color, height, width, isActive }) {\n  return (\n    div className=\"card\">\n      \u003CAvatar\n        color={color}\n        height={height}\n        width={width}\n        isActive={isActive}\n      />\n    \u003C/div>\n  );\n}\n",[179,31552,31553,31580,31586,31598,31605,31615,31625,31634,31644,31648,31656,31660],{"__ignoreMap":307},[1736,31554,31555,31557,31560,31562,31564,31566,31568,31570,31573,31575,31578],{"class":1738,"line":1739},[1736,31556,7745],{"class":4866},[1736,31558,31559],{"class":2674}," Card",[1736,31561,7233],{"class":1912},[1736,31563,31425],{"class":5036},[1736,31565,829],{"class":1912},[1736,31567,31430],{"class":5036},[1736,31569,829],{"class":1912},[1736,31571,31572],{"class":5036},"width",[1736,31574,829],{"class":1912},[1736,31576,31577],{"class":5036},"isActive",[1736,31579,31433],{"class":1912},[1736,31581,31582,31584],{"class":1738,"line":748},[1736,31583,6685],{"class":4866},[1736,31585,6688],{"class":1912},[1736,31587,31588,31591,31593,31596],{"class":1738,"line":756},[1736,31589,31590],{"class":1912},"    div className",[1736,31592,5062],{"class":4866},[1736,31594,31595],{"class":1935},"\"card\"",[1736,31597,6663],{"class":4866},[1736,31599,31600,31602],{"class":1738,"line":1755},[1736,31601,6710],{"class":1912},[1736,31603,31604],{"class":1918},"Avatar\n",[1736,31606,31607,31610,31612],{"class":1738,"line":1761},[1736,31608,31609],{"class":2674},"        color",[1736,31611,5062],{"class":4866},[1736,31613,31614],{"class":1912},"{color}\n",[1736,31616,31617,31620,31622],{"class":1738,"line":1767},[1736,31618,31619],{"class":2674},"        height",[1736,31621,5062],{"class":4866},[1736,31623,31624],{"class":1912},"{height}\n",[1736,31626,31627,31629,31631],{"class":1738,"line":1772},[1736,31628,9175],{"class":2674},[1736,31630,5062],{"class":4866},[1736,31632,31633],{"class":1912},"{width}\n",[1736,31635,31636,31639,31641],{"class":1738,"line":1778},[1736,31637,31638],{"class":2674},"        isActive",[1736,31640,5062],{"class":4866},[1736,31642,31643],{"class":1912},"{isActive}\n",[1736,31645,31646],{"class":1738,"line":1784},[1736,31647,9195],{"class":1912},[1736,31649,31650,31652,31654],{"class":1738,"line":1790},[1736,31651,6744],{"class":4866},[1736,31653,6697],{"class":1912},[1736,31655,6663],{"class":4866},[1736,31657,31658],{"class":1738,"line":1796},[1736,31659,7956],{"class":1912},[1736,31661,31662],{"class":1738,"line":2353},[1736,31663,1976],{"class":1912},[11,31665,31666,31667,31670],{},"Here we just pass in all the props and then use the spread operator to spread them so that the ",[179,31668,31669],{},"Avatar"," component has access to all the props without having to write them out individually.",[299,31672,31674],{"className":4894,"code":31673,"language":4896,"meta":307,"style":307},"function Card(props) {\n  return (\n    \u003Cdiv className=\"card\">\n      \u003CAvatar {...props} />\n    \u003C/div>\n  )\n}\n",[179,31675,31676,31688,31694,31708,31722,31730,31734],{"__ignoreMap":307},[1736,31677,31678,31680,31682,31684,31686],{"class":1738,"line":1739},[1736,31679,7745],{"class":4866},[1736,31681,31559],{"class":2674},[1736,31683,7751],{"class":1912},[1736,31685,31463],{"class":5036},[1736,31687,7246],{"class":1912},[1736,31689,31690,31692],{"class":1738,"line":748},[1736,31691,6685],{"class":4866},[1736,31693,6688],{"class":1912},[1736,31695,31696,31698,31700,31702,31704,31706],{"class":1738,"line":756},[1736,31697,6693],{"class":1912},[1736,31699,6697],{"class":6696},[1736,31701,6700],{"class":2674},[1736,31703,5062],{"class":4866},[1736,31705,31595],{"class":1935},[1736,31707,6663],{"class":1912},[1736,31709,31710,31712,31714,31717,31719],{"class":1738,"line":1755},[1736,31711,6710],{"class":1912},[1736,31713,31669],{"class":1918},[1736,31715,31716],{"class":1912}," {",[1736,31718,11965],{"class":4866},[1736,31720,31721],{"class":1912},"props} />\n",[1736,31723,31724,31726,31728],{"class":1738,"line":1761},[1736,31725,6744],{"class":1912},[1736,31727,6697],{"class":6696},[1736,31729,6663],{"class":1912},[1736,31731,31732],{"class":1738,"line":1767},[1736,31733,6753],{"class":1912},[1736,31735,31736],{"class":1738,"line":1772},[1736,31737,1976],{"class":1912},[23,31739,31741],{"id":31740},"reacts-children","React's Children",[11,31743,31744],{},"This is similar to what we call slots in Vue. We pass in the children prop to the function and then use it so that anything rendered inside the Card component will be rendered.",[299,31746,31748],{"className":4894,"code":31747,"language":4896,"meta":307,"style":307},"function Card({ children }) {\n  return \u003Cdiv className=\"card\">{children}\u003C/div>\n}\n",[179,31749,31750,31763,31784],{"__ignoreMap":307},[1736,31751,31752,31754,31756,31758,31761],{"class":1738,"line":1739},[1736,31753,7745],{"class":4866},[1736,31755,31559],{"class":2674},[1736,31757,7233],{"class":1912},[1736,31759,31760],{"class":5036},"children",[1736,31762,31433],{"class":1912},[1736,31764,31765,31767,31769,31771,31773,31775,31777,31780,31782],{"class":1738,"line":748},[1736,31766,6685],{"class":4866},[1736,31768,10193],{"class":1912},[1736,31770,6697],{"class":6696},[1736,31772,6700],{"class":2674},[1736,31774,5062],{"class":4866},[1736,31776,31595],{"class":1935},[1736,31778,31779],{"class":1912},">{children}\u003C/",[1736,31781,6697],{"class":6696},[1736,31783,6663],{"class":1912},[1736,31785,31786],{"class":1738,"line":756},[1736,31787,1976],{"class":1912},[11,31789,31790],{},"We can then easily use this component to render different components inside. Cruises:",[299,31792,31794],{"className":4894,"code":31793,"language":4896,"meta":307,"style":307},"export default function Cruises() {\n  return (\n    \u003CCard>\n      \u003CCruises />\n    \u003C/Card>\n  )\n}\n",[179,31795,31796,31809,31815,31824,31833,31841,31845],{"__ignoreMap":307},[1736,31797,31798,31800,31802,31804,31807],{"class":1738,"line":1739},[1736,31799,6632],{"class":4866},[1736,31801,30438],{"class":4866},[1736,31803,6674],{"class":4866},[1736,31805,31806],{"class":2674}," Cruises",[1736,31808,6680],{"class":1912},[1736,31810,31811,31813],{"class":1738,"line":748},[1736,31812,6685],{"class":4866},[1736,31814,6688],{"class":1912},[1736,31816,31817,31819,31822],{"class":1738,"line":756},[1736,31818,6693],{"class":1912},[1736,31820,31821],{"class":1918},"Card",[1736,31823,6663],{"class":1912},[1736,31825,31826,31828,31831],{"class":1738,"line":1755},[1736,31827,6710],{"class":1912},[1736,31829,31830],{"class":1918},"Cruises",[1736,31832,6739],{"class":1912},[1736,31834,31835,31837,31839],{"class":1738,"line":1761},[1736,31836,6744],{"class":1912},[1736,31838,31821],{"class":1918},[1736,31840,6663],{"class":1912},[1736,31842,31843],{"class":1738,"line":1767},[1736,31844,6753],{"class":1912},[1736,31846,31847],{"class":1738,"line":1772},[1736,31848,1976],{"class":1912},[11,31850,31851],{},"Flights:",[299,31853,31855],{"className":4894,"code":31854,"language":4896,"meta":307,"style":307},"export default function Cruises() {\n  return (\n    \u003CCard>\n      \u003CFlights />\n    \u003C/Card>\n  )\n}\n",[179,31856,31857,31869,31875,31883,31892,31900,31904],{"__ignoreMap":307},[1736,31858,31859,31861,31863,31865,31867],{"class":1738,"line":1739},[1736,31860,6632],{"class":4866},[1736,31862,30438],{"class":4866},[1736,31864,6674],{"class":4866},[1736,31866,31806],{"class":2674},[1736,31868,6680],{"class":1912},[1736,31870,31871,31873],{"class":1738,"line":748},[1736,31872,6685],{"class":4866},[1736,31874,6688],{"class":1912},[1736,31876,31877,31879,31881],{"class":1738,"line":756},[1736,31878,6693],{"class":1912},[1736,31880,31821],{"class":1918},[1736,31882,6663],{"class":1912},[1736,31884,31885,31887,31890],{"class":1738,"line":1755},[1736,31886,6710],{"class":1912},[1736,31888,31889],{"class":1918},"Flights",[1736,31891,6739],{"class":1912},[1736,31893,31894,31896,31898],{"class":1738,"line":1761},[1736,31895,6744],{"class":1912},[1736,31897,31821],{"class":1918},[1736,31899,6663],{"class":1912},[1736,31901,31902],{"class":1738,"line":1767},[1736,31903,6753],{"class":1912},[1736,31905,31906],{"class":1738,"line":1772},[1736,31907,1976],{"class":1912},[23,31909,3294],{"id":3293},[11,31911,31912],{},"Understanding how React works is really helpful as well as being able to compare the differences to Vue. Overall React is really just JavaScript and Vue just adds some magic to make things much easier. The more you work with React the easier it gets. Still so much more to learn though. This is just the beginning.",[11,31914,31915],{},"Disclaimer: I am still only learning so if you see anything wrong here then just let me know :)",[11,31917,31918],{},"Courses I am taking to enhance my React Journey:",[70,31920,31921,31929,31937],{},[73,31922,31923,31924],{},"Kent C Dodds ",[15,31925,31928],{"href":31926,"rel":31927},"https://egghead.io/lessons/react-a-beginners-guide-to-react-introduction",[19],"React a Beginners Guide",[73,31930,31931,31932],{},"Brian Holt ",[15,31933,31936],{"href":31934,"rel":31935},"https://frontendmasters.com/courses/complete-react-v5/",[19],"Complete React v5",[73,31938,31923,31939],{},[15,31940,31943],{"href":31941,"rel":31942},"https://epicreact.dev/",[19],"Epic React",[2011,31945,31946],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":31948},[31949,31952,31953,31954,31955,31956,31957,31958,31962,31963,31964],{"id":30348,"depth":748,"text":30349,"children":31950},[31951],{"id":30467,"depth":756,"text":30468},{"id":30500,"depth":748,"text":30501},{"id":30564,"depth":748,"text":30565},{"id":30645,"depth":748,"text":30646},{"id":30722,"depth":748,"text":30723},{"id":30769,"depth":748,"text":30770},{"id":30894,"depth":748,"text":30895},{"id":31257,"depth":748,"text":31258,"children":31959},[31960,31961],{"id":31264,"depth":756,"text":31265},{"id":31317,"depth":756,"text":31318},{"id":31390,"depth":748,"text":31391},{"id":31740,"depth":748,"text":31741},{"id":3293,"depth":748,"text":3294},"2021-03-21","photo-1581276879432-15e50529f34b?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop",{},"/blog/learning-react",{"title":30340,"description":30345},"blog/learning-react",[5221],"vygniisTkbKB7okJZpbThkTbCgC_3E7ljqxL0lSItUg",{"id":31974,"title":31975,"body":31976,"canonical":32968,"date":25551,"description":32975,"extension":786,"featured":787,"image":788,"meta":32976,"navigation":790,"ogimage":788,"path":32977,"provider":788,"published":790,"seo":32978,"stem":32979,"tags":32980,"url":788,"__hash__":32981},"blog/blog/letting-playwright-mcp-explore-your-site.md","Letting Playwright MCP explore your site and write your tests",{"type":8,"value":31977,"toc":32973},[31978,31981,31984,31987,31997,32059,32069,32190,32193,32202,32205,32208,32211,32214,32217,32220,32223,32226,32243,32246,32249,32252,32255,32868,32871,32874,32885,32888,32891,32894,32908,32911,32914,32917,32926,32929,32938,32960,32962,32970],[11,31979,31980],{},"What if your tests could write themselves — just by using your app like a real user?",[11,31982,31983],{},"In this post, we explore how the Playwright MCP (Model Context Protocol) in Agent Mode can autonomously navigate your app, discover key functionality, and generate runnable tests — no manual scripting required.",[11,31985,31986],{},"We’ll walk through a live demo of generating and running a test against a Movies app, highlighting how the MCP uncovers edge cases, builds coverage, and even surfaces bugs you might miss.",[11,31988,31989,31990,31993,31994,891],{},"🔧 Setting the Stage\nFor this demo, I’ve got the MCP Playwright server running locally inside my ",[179,31991,31992],{},".vscode"," project folder in a file called ",[179,31995,31996],{},"mcp.json",[299,31998,32000],{"className":1903,"code":31999,"language":1905,"meta":307,"style":307},"{\n    \"servers\": {\n        \"playwright\": {\n            \"command\": \"npx\",\n            \"args\": [\n                \"@playwright/mcp@latest\"\n            ]\n        }\n    }\n}\n",[179,32001,32002,32006,32012,32019,32030,32037,32042,32047,32051,32055],{"__ignoreMap":307},[1736,32003,32004],{"class":1738,"line":1739},[1736,32005,1913],{"class":1912},[1736,32007,32008,32010],{"class":1738,"line":748},[1736,32009,26028],{"class":1918},[1736,32011,1922],{"class":1912},[1736,32013,32014,32017],{"class":1738,"line":756},[1736,32015,32016],{"class":1918},"        \"playwright\"",[1736,32018,1922],{"class":1912},[1736,32020,32021,32024,32026,32028],{"class":1738,"line":1755},[1736,32022,32023],{"class":1918},"            \"command\"",[1736,32025,3065],{"class":1912},[1736,32027,18855],{"class":1935},[1736,32029,1939],{"class":1912},[1736,32031,32032,32035],{"class":1738,"line":1761},[1736,32033,32034],{"class":1918},"            \"args\"",[1736,32036,1930],{"class":1912},[1736,32038,32039],{"class":1738,"line":1767},[1736,32040,32041],{"class":1935},"                \"@playwright/mcp@latest\"\n",[1736,32043,32044],{"class":1738,"line":1772},[1736,32045,32046],{"class":1912},"            ]\n",[1736,32048,32049],{"class":1738,"line":1778},[1736,32050,26121],{"class":1912},[1736,32052,32053],{"class":1738,"line":1784},[1736,32054,9853],{"class":1912},[1736,32056,32057],{"class":1738,"line":1790},[1736,32058,1976],{"class":1912},[11,32060,32061,32062,32065,32066,1087],{},"I’ve prepared a simple test prompt which is located in the ",[179,32063,32064],{},".github"," folder and named it ",[179,32067,32068],{},"generate_tests.prompt.md",[299,32070,32072],{"className":5843,"code":32071,"language":786,"meta":307,"style":307},"---\ntools: ['playwright']\nmode: 'agent'\n---\n\n- You are a playwright test generator.\n- You are given a scenario and you need to generate a playwright test for it.\n- DO NOT generate test code based on the scenario alone. \n- DO run steps one by one using the tools provided by the Playwright MCP.\n- When asked to explore a website:\n  1. Navigate to the specified URL\n  2. Explore 1 key functionality of the site and when finished close the browser.\n  3. Implement a Playwright TypeScript test that uses @playwright/test based on message history using Playwright's best practices including role based locators, auto retrying assertions and with no added timeouts unless necessary as Playwright has built in retries and autowaiting if the correct locators and assertions are used.\n- Save generated test file in the tests directory\n- Execute the test file and iterate until the test passes\n- Include appropriate assertions to verify the expected behavior\n- Structure tests properly with descriptive test titles and comments\n",[179,32073,32074,32079,32091,32101,32105,32109,32115,32121,32127,32133,32140,32148,32156,32164,32170,32176,32183],{"__ignoreMap":307},[1736,32075,32076],{"class":1738,"line":1739},[1736,32077,32078],{"class":1912},"---\n",[1736,32080,32081,32084,32086,32089],{"class":1738,"line":748},[1736,32082,32083],{"class":6696},"tools",[1736,32085,16439],{"class":1912},[1736,32087,32088],{"class":1935},"'playwright'",[1736,32090,8420],{"class":1912},[1736,32092,32093,32096,32098],{"class":1738,"line":756},[1736,32094,32095],{"class":6696},"mode",[1736,32097,3065],{"class":1912},[1736,32099,32100],{"class":1935},"'agent'\n",[1736,32102,32103],{"class":1738,"line":1755},[1736,32104,32078],{"class":1912},[1736,32106,32107],{"class":1738,"line":1761},[1736,32108,1747],{"emptyLinePlaceholder":790},[1736,32110,32111,32113],{"class":1738,"line":1767},[1736,32112,9419],{"class":5036},[1736,32114,25141],{"class":1912},[1736,32116,32117,32119],{"class":1738,"line":1772},[1736,32118,9419],{"class":5036},[1736,32120,25148],{"class":1912},[1736,32122,32123,32125],{"class":1738,"line":1778},[1736,32124,9419],{"class":5036},[1736,32126,25155],{"class":1912},[1736,32128,32129,32131],{"class":1738,"line":1784},[1736,32130,9419],{"class":5036},[1736,32132,25162],{"class":1912},[1736,32134,32135,32137],{"class":1738,"line":1790},[1736,32136,9419],{"class":5036},[1736,32138,32139],{"class":1912}," When asked to explore a website:\n",[1736,32141,32142,32145],{"class":1738,"line":1796},[1736,32143,32144],{"class":5036},"  1.",[1736,32146,32147],{"class":1912}," Navigate to the specified URL\n",[1736,32149,32150,32153],{"class":1738,"line":2353},[1736,32151,32152],{"class":5036},"  2.",[1736,32154,32155],{"class":1912}," Explore 1 key functionality of the site and when finished close the browser.\n",[1736,32157,32158,32161],{"class":1738,"line":2358},[1736,32159,32160],{"class":5036},"  3.",[1736,32162,32163],{"class":1912}," Implement a Playwright TypeScript test that uses @playwright/test based on message history using Playwright's best practices including role based locators, auto retrying assertions and with no added timeouts unless necessary as Playwright has built in retries and autowaiting if the correct locators and assertions are used.\n",[1736,32165,32166,32168],{"class":1738,"line":2364},[1736,32167,9419],{"class":5036},[1736,32169,25176],{"class":1912},[1736,32171,32172,32174],{"class":1738,"line":2370},[1736,32173,9419],{"class":5036},[1736,32175,25183],{"class":1912},[1736,32177,32178,32180],{"class":1738,"line":2376},[1736,32179,9419],{"class":5036},[1736,32181,32182],{"class":1912}," Include appropriate assertions to verify the expected behavior\n",[1736,32184,32185,32187],{"class":1738,"line":2381},[1736,32186,9419],{"class":5036},[1736,32188,32189],{"class":1912}," Structure tests properly with descriptive test titles and comments\n",[11,32191,32192],{},"Then in VS Code I use Agent Mode and make sure my prompt is added to context and then I simply type:",[299,32194,32196],{"className":5843,"code":32195,"language":786,"meta":307,"style":307},"Explore https://debs-obrien.github.io/playwright-movies-app\n",[179,32197,32198],{"__ignoreMap":307},[1736,32199,32200],{"class":1738,"line":1739},[1736,32201,32195],{"class":1912},[11,32203,32204],{},"Agent mode uses the Playwright MCP to navigate to the site and use the browser to explore the app like a real user.",[11,32206,32207],{},"🧠 Goal: Let the agent freely navigate, discover functionality, and generate tests automatically based on its interactions.",[11,32209,32210],{},"🧪 Exploration Begins\nOnce the agent starts exploring, the first thing it tries is the search feature. It types “Star Wars” into the search bar — and immediately, we uncover a bug.",[11,32212,32213],{},"The search results show “Star Wars”, but the movie title returned is “Kill”. That’s clearly wrong.",[11,32215,32216],{},"This is an edge case I hadn’t noticed in manual testing. I’d previously searched terms like Garfield, Deadpool, and Avengers — and everything worked fine. But now, thanks to the agent’s autonomous behavior, I’ve uncovered a regression.",[11,32218,32219],{},"✅ Result: The agent discovered a search issue — something I’d missed entirely.",[11,32221,32222],{},"🌓 Theme Toggling and UI Coverage\nNext, the agent toggles the app’s theme switch — switching between dark and light mode. It verifies that the toggle works, clicks through navigation links, and continues its exploratory crawl.",[11,32224,32225],{},"After wrapping up the interactions, the agent summarizes its findings:",[70,32227,32228,32231,32234,32237,32240],{},[73,32229,32230],{},"Homepage",[73,32232,32233],{},"Search functionality",[73,32235,32236],{},"Movie details page",[73,32238,32239],{},"Theme toggle",[73,32241,32242],{},"Navigation",[11,32244,32245],{},"From that list, it selects search functionality as the focus for the test it will generate.",[11,32247,32248],{},"🎯 Note: You can tell the agent how many tests you want. In this case, I requested just one for the demo.",[11,32250,32251],{},"🧾 Test Generation & Execution\nThe agent generates a full Playwright test file based on the interactions. It even fixes a lint error automatically before running the test.",[11,32253,32254],{},"Here’s the test it generated:",[299,32256,32258],{"className":25266,"code":32257,"language":25268,"meta":307,"style":307},"import { test, expect } from '@playwright/test';\n\ntest.describe('Movie search', () => {\n  test('Search for a movie by title', async ({ page }) => {\n    // Navigate to the movies app\n    await page.goto('https://debs-obrien.github.io/playwright-movies-app');\n\n    // Click on the search button to activate the search input\n    await page.getByRole('search').click();\n    \n    // Type 'Star Wars' into the search input and press Enter\n    const searchTerm = 'Star Wars';\n    await page.getByRole('textbox', { name: 'Search Input' }).fill(searchTerm);\n    await page.getByRole('textbox', { name: 'Search Input' }).press('Enter');\n\n    // Verify we're on the search results page with correct title\n    await expect(page).toHaveTitle(`${searchTerm} - Search Results`);\n    \n    // Verify the search results heading contains the search term\n    await expect(page.getByRole('heading', { level: 1 })).toHaveText(searchTerm);\n    await expect(page.getByRole('heading', { name: 'search results', level: 2 })).toBeVisible();\n    \n    // Verify that search results are displayed\n    await expect(page.getByRole('list', { name: 'movies' })).toBeVisible();\n    \n    // Click on a movie from search results\n    const firstMovie = page.getByRole('list', { name: 'movies' }).getByRole('link').first();\n    const movieTitleElement = firstMovie.getByRole('heading', { level: 2 });\n    const movieTitle = await movieTitleElement.textContent() || '';\n    await firstMovie.click();\n\n    // Verify that the movie details page is loaded with the correct title\n    await expect(page.getByRole('heading', { level: 1 })).toHaveText(movieTitle);\n    \n    // Verify movie details sections are present\n    await expect(page.getByText('The Synopsis')).toBeVisible();\n    await expect(page.getByText('The Cast')).toBeVisible();\n\n    // Verify recommended movies section is present\n    await expect(page.getByRole('heading', { name: 'Recommended Movies' })).toBeVisible();\n    \n    // Go back to search results\n    await page.getByRole('button', { name: 'Back' }).click();\n    \n    // Verify we're back on the search results page\n    await expect(page.getByRole('heading', { level: 1 })).toHaveText(searchTerm);\n  });\n});\n",[179,32259,32260,32272,32276,32294,32318,32323,32337,32341,32346,32364,32369,32374,32388,32411,32437,32441,32446,32470,32474,32479,32505,32534,32538,32543,32568,32572,32577,32613,32638,32664,32674,32678,32683,32708,32712,32717,32739,32760,32764,32769,32794,32798,32803,32826,32830,32835,32859,32864],{"__ignoreMap":307},[1736,32261,32262,32264,32266,32268,32270],{"class":1738,"line":1739},[1736,32263,4996],{"class":4866},[1736,32265,25277],{"class":1912},[1736,32267,5002],{"class":4866},[1736,32269,25282],{"class":1935},[1736,32271,7682],{"class":1912},[1736,32273,32274],{"class":1738,"line":748},[1736,32275,1747],{"emptyLinePlaceholder":790},[1736,32277,32278,32281,32283,32285,32288,32290,32292],{"class":1738,"line":756},[1736,32279,32280],{"class":1912},"test.",[1736,32282,17717],{"class":2674},[1736,32284,7751],{"class":1912},[1736,32286,32287],{"class":1935},"'Movie search'",[1736,32289,10524],{"class":1912},[1736,32291,7013],{"class":4866},[1736,32293,4914],{"class":1912},[1736,32295,32296,32299,32301,32304,32306,32308,32310,32312,32314,32316],{"class":1738,"line":1755},[1736,32297,32298],{"class":2674},"  test",[1736,32300,7751],{"class":1912},[1736,32302,32303],{"class":1935},"'Search for a movie by title'",[1736,32305,829],{"class":1912},[1736,32307,15790],{"class":4866},[1736,32309,17735],{"class":1912},[1736,32311,25307],{"class":5036},[1736,32313,7778],{"class":1912},[1736,32315,7013],{"class":4866},[1736,32317,4914],{"class":1912},[1736,32319,32320],{"class":1738,"line":1761},[1736,32321,32322],{"class":6820},"    // Navigate to the movies app\n",[1736,32324,32325,32327,32329,32331,32333,32335],{"class":1738,"line":1767},[1736,32326,16153],{"class":4866},[1736,32328,22665],{"class":1912},[1736,32330,25328],{"class":2674},[1736,32332,7751],{"class":1912},[1736,32334,25333],{"class":1935},[1736,32336,16106],{"class":1912},[1736,32338,32339],{"class":1738,"line":1772},[1736,32340,1747],{"emptyLinePlaceholder":790},[1736,32342,32343],{"class":1738,"line":1778},[1736,32344,32345],{"class":6820},"    // Click on the search button to activate the search input\n",[1736,32347,32348,32350,32352,32354,32356,32358,32360,32362],{"class":1738,"line":1784},[1736,32349,16153],{"class":4866},[1736,32351,22665],{"class":1912},[1736,32353,1032],{"class":2674},[1736,32355,7751],{"class":1912},[1736,32357,25357],{"class":1935},[1736,32359,911],{"class":1912},[1736,32361,10804],{"class":2674},[1736,32363,15752],{"class":1912},[1736,32365,32366],{"class":1738,"line":1790},[1736,32367,32368],{"class":1912},"    \n",[1736,32370,32371],{"class":1738,"line":1796},[1736,32372,32373],{"class":6820},"    // Type 'Star Wars' into the search input and press Enter\n",[1736,32375,32376,32378,32381,32383,32386],{"class":1738,"line":2353},[1736,32377,14497],{"class":4866},[1736,32379,32380],{"class":1918}," searchTerm",[1736,32382,4911],{"class":4866},[1736,32384,32385],{"class":1935}," 'Star Wars'",[1736,32387,7682],{"class":1912},[1736,32389,32390,32392,32394,32396,32398,32400,32402,32404,32406,32408],{"class":1738,"line":2358},[1736,32391,16153],{"class":4866},[1736,32393,22665],{"class":1912},[1736,32395,1032],{"class":2674},[1736,32397,7751],{"class":1912},[1736,32399,25385],{"class":1935},[1736,32401,10685],{"class":1912},[1736,32403,25390],{"class":1935},[1736,32405,25393],{"class":1912},[1736,32407,25396],{"class":2674},[1736,32409,32410],{"class":1912},"(searchTerm);\n",[1736,32412,32413,32415,32417,32419,32421,32423,32425,32427,32429,32431,32433,32435],{"class":1738,"line":2364},[1736,32414,16153],{"class":4866},[1736,32416,22665],{"class":1912},[1736,32418,1032],{"class":2674},[1736,32420,7751],{"class":1912},[1736,32422,25385],{"class":1935},[1736,32424,10685],{"class":1912},[1736,32426,25390],{"class":1935},[1736,32428,25393],{"class":1912},[1736,32430,25424],{"class":2674},[1736,32432,7751],{"class":1912},[1736,32434,25429],{"class":1935},[1736,32436,16106],{"class":1912},[1736,32438,32439],{"class":1738,"line":2370},[1736,32440,1747],{"emptyLinePlaceholder":790},[1736,32442,32443],{"class":1738,"line":2376},[1736,32444,32445],{"class":6820},"    // Verify we're on the search results page with correct title\n",[1736,32447,32448,32450,32452,32455,32458,32460,32462,32465,32468],{"class":1738,"line":2381},[1736,32449,16153],{"class":4866},[1736,32451,25447],{"class":2674},[1736,32453,32454],{"class":1912},"(page).",[1736,32456,32457],{"class":2674},"toHaveTitle",[1736,32459,7751],{"class":1912},[1736,32461,16967],{"class":1935},[1736,32463,32464],{"class":1912},"searchTerm",[1736,32466,32467],{"class":1935},"} - Search Results`",[1736,32469,16106],{"class":1912},[1736,32471,32472],{"class":1738,"line":2387},[1736,32473,32368],{"class":1912},[1736,32475,32476],{"class":1738,"line":2393},[1736,32477,32478],{"class":6820},"    // Verify the search results heading contains the search term\n",[1736,32480,32481,32483,32485,32487,32489,32491,32493,32496,32498,32500,32503],{"class":1738,"line":2398},[1736,32482,16153],{"class":4866},[1736,32484,25447],{"class":2674},[1736,32486,25450],{"class":1912},[1736,32488,1032],{"class":2674},[1736,32490,7751],{"class":1912},[1736,32492,25457],{"class":1935},[1736,32494,32495],{"class":1912},", { level: ",[1736,32497,10249],{"class":1918},[1736,32499,25470],{"class":1912},[1736,32501,32502],{"class":2674},"toHaveText",[1736,32504,32410],{"class":1912},[1736,32506,32507,32509,32511,32513,32515,32517,32519,32521,32524,32526,32528,32530,32532],{"class":1738,"line":2404},[1736,32508,16153],{"class":4866},[1736,32510,25447],{"class":2674},[1736,32512,25450],{"class":1912},[1736,32514,1032],{"class":2674},[1736,32516,7751],{"class":1912},[1736,32518,25457],{"class":1935},[1736,32520,10685],{"class":1912},[1736,32522,32523],{"class":1935},"'search results'",[1736,32525,25465],{"class":1912},[1736,32527,10820],{"class":1918},[1736,32529,25470],{"class":1912},[1736,32531,25473],{"class":2674},[1736,32533,15752],{"class":1912},[1736,32535,32536],{"class":1738,"line":6959},[1736,32537,32368],{"class":1912},[1736,32539,32540],{"class":1738,"line":7296},[1736,32541,32542],{"class":6820},"    // Verify that search results are displayed\n",[1736,32544,32545,32547,32549,32551,32553,32555,32557,32559,32562,32564,32566],{"class":1738,"line":7305},[1736,32546,16153],{"class":4866},[1736,32548,25447],{"class":2674},[1736,32550,25450],{"class":1912},[1736,32552,1032],{"class":2674},[1736,32554,7751],{"class":1912},[1736,32556,27451],{"class":1935},[1736,32558,10685],{"class":1912},[1736,32560,32561],{"class":1935},"'movies'",[1736,32563,25470],{"class":1912},[1736,32565,25473],{"class":2674},[1736,32567,15752],{"class":1912},[1736,32569,32570],{"class":1738,"line":7310},[1736,32571,32368],{"class":1912},[1736,32573,32574],{"class":1738,"line":9659},[1736,32575,32576],{"class":6820},"    // Click on a movie from search results\n",[1736,32578,32579,32581,32584,32586,32588,32590,32592,32594,32596,32598,32600,32602,32604,32606,32608,32611],{"class":1738,"line":9680},[1736,32580,14497],{"class":4866},[1736,32582,32583],{"class":1918}," firstMovie",[1736,32585,4911],{"class":4866},[1736,32587,22665],{"class":1912},[1736,32589,1032],{"class":2674},[1736,32591,7751],{"class":1912},[1736,32593,27451],{"class":1935},[1736,32595,10685],{"class":1912},[1736,32597,32561],{"class":1935},[1736,32599,25393],{"class":1912},[1736,32601,1032],{"class":2674},[1736,32603,7751],{"class":1912},[1736,32605,27009],{"class":1935},[1736,32607,911],{"class":1912},[1736,32609,32610],{"class":2674},"first",[1736,32612,15752],{"class":1912},[1736,32614,32615,32617,32620,32622,32625,32627,32629,32631,32633,32635],{"class":1738,"line":9699},[1736,32616,14497],{"class":4866},[1736,32618,32619],{"class":1918}," movieTitleElement",[1736,32621,4911],{"class":4866},[1736,32623,32624],{"class":1912}," firstMovie.",[1736,32626,1032],{"class":2674},[1736,32628,7751],{"class":1912},[1736,32630,25457],{"class":1935},[1736,32632,32495],{"class":1912},[1736,32634,10820],{"class":1918},[1736,32636,32637],{"class":1912}," });\n",[1736,32639,32640,32642,32645,32647,32649,32652,32655,32657,32659,32662],{"class":1738,"line":9704},[1736,32641,14497],{"class":4866},[1736,32643,32644],{"class":1918}," movieTitle",[1736,32646,4911],{"class":4866},[1736,32648,18389],{"class":4866},[1736,32650,32651],{"class":1912}," movieTitleElement.",[1736,32653,32654],{"class":2674},"textContent",[1736,32656,7840],{"class":1912},[1736,32658,7792],{"class":4866},[1736,32660,32661],{"class":1935}," ''",[1736,32663,7682],{"class":1912},[1736,32665,32666,32668,32670,32672],{"class":1738,"line":9715},[1736,32667,16153],{"class":4866},[1736,32669,32624],{"class":1912},[1736,32671,10804],{"class":2674},[1736,32673,15752],{"class":1912},[1736,32675,32676],{"class":1738,"line":9727},[1736,32677,1747],{"emptyLinePlaceholder":790},[1736,32679,32680],{"class":1738,"line":9739},[1736,32681,32682],{"class":6820},"    // Verify that the movie details page is loaded with the correct title\n",[1736,32684,32685,32687,32689,32691,32693,32695,32697,32699,32701,32703,32705],{"class":1738,"line":9750},[1736,32686,16153],{"class":4866},[1736,32688,25447],{"class":2674},[1736,32690,25450],{"class":1912},[1736,32692,1032],{"class":2674},[1736,32694,7751],{"class":1912},[1736,32696,25457],{"class":1935},[1736,32698,32495],{"class":1912},[1736,32700,10249],{"class":1918},[1736,32702,25470],{"class":1912},[1736,32704,32502],{"class":2674},[1736,32706,32707],{"class":1912},"(movieTitle);\n",[1736,32709,32710],{"class":1738,"line":9761},[1736,32711,32368],{"class":1912},[1736,32713,32714],{"class":1738,"line":9767},[1736,32715,32716],{"class":6820},"    // Verify movie details sections are present\n",[1736,32718,32719,32721,32723,32725,32727,32729,32732,32735,32737],{"class":1738,"line":9778},[1736,32720,16153],{"class":4866},[1736,32722,25447],{"class":2674},[1736,32724,25450],{"class":1912},[1736,32726,1039],{"class":2674},[1736,32728,7751],{"class":1912},[1736,32730,32731],{"class":1935},"'The Synopsis'",[1736,32733,32734],{"class":1912},")).",[1736,32736,25473],{"class":2674},[1736,32738,15752],{"class":1912},[1736,32740,32741,32743,32745,32747,32749,32751,32754,32756,32758],{"class":1738,"line":9799},[1736,32742,16153],{"class":4866},[1736,32744,25447],{"class":2674},[1736,32746,25450],{"class":1912},[1736,32748,1039],{"class":2674},[1736,32750,7751],{"class":1912},[1736,32752,32753],{"class":1935},"'The Cast'",[1736,32755,32734],{"class":1912},[1736,32757,25473],{"class":2674},[1736,32759,15752],{"class":1912},[1736,32761,32762],{"class":1738,"line":9804},[1736,32763,1747],{"emptyLinePlaceholder":790},[1736,32765,32766],{"class":1738,"line":9814},[1736,32767,32768],{"class":6820},"    // Verify recommended movies section is present\n",[1736,32770,32771,32773,32775,32777,32779,32781,32783,32785,32788,32790,32792],{"class":1738,"line":9826},[1736,32772,16153],{"class":4866},[1736,32774,25447],{"class":2674},[1736,32776,25450],{"class":1912},[1736,32778,1032],{"class":2674},[1736,32780,7751],{"class":1912},[1736,32782,25457],{"class":1935},[1736,32784,10685],{"class":1912},[1736,32786,32787],{"class":1935},"'Recommended Movies'",[1736,32789,25470],{"class":1912},[1736,32791,25473],{"class":2674},[1736,32793,15752],{"class":1912},[1736,32795,32796],{"class":1738,"line":9838},[1736,32797,32368],{"class":1912},[1736,32799,32800],{"class":1738,"line":9850},[1736,32801,32802],{"class":6820},"    // Go back to search results\n",[1736,32804,32805,32807,32809,32811,32813,32815,32817,32820,32822,32824],{"class":1738,"line":9856},[1736,32806,16153],{"class":4866},[1736,32808,22665],{"class":1912},[1736,32810,1032],{"class":2674},[1736,32812,7751],{"class":1912},[1736,32814,10682],{"class":1935},[1736,32816,10685],{"class":1912},[1736,32818,32819],{"class":1935},"'Back'",[1736,32821,25393],{"class":1912},[1736,32823,10804],{"class":2674},[1736,32825,15752],{"class":1912},[1736,32827,32828],{"class":1738,"line":9861},[1736,32829,32368],{"class":1912},[1736,32831,32832],{"class":1738,"line":9870},[1736,32833,32834],{"class":6820},"    // Verify we're back on the search results page\n",[1736,32836,32837,32839,32841,32843,32845,32847,32849,32851,32853,32855,32857],{"class":1738,"line":9881},[1736,32838,16153],{"class":4866},[1736,32840,25447],{"class":2674},[1736,32842,25450],{"class":1912},[1736,32844,1032],{"class":2674},[1736,32846,7751],{"class":1912},[1736,32848,25457],{"class":1935},[1736,32850,32495],{"class":1912},[1736,32852,10249],{"class":1918},[1736,32854,25470],{"class":1912},[1736,32856,32502],{"class":2674},[1736,32858,32410],{"class":1912},[1736,32860,32861],{"class":1738,"line":9892},[1736,32862,32863],{"class":1912},"  });\n",[1736,32865,32866],{"class":1738,"line":9903},[1736,32867,17657],{"class":1912},[11,32869,32870],{},"Once generated, it opens a terminal and runs the test. It passes ✅.",[11,32872,32873],{},"We then open the Trace Viewer in VS Code to visually inspect the steps taken:",[70,32875,32876,32879,32882],{},[73,32877,32878],{},"It searched for Star Wars.",[73,32880,32881],{},"Clicked through results like Deadpool.",[73,32883,32884],{},"Verified titles on the movie details page.",[11,32886,32887],{},"It’s a full cycle: exploration → generation → execution → review.",[11,32889,32890],{},"💡 Why This Matters\nThis might seem like magic — but it’s a real example of AI-assisted development.",[11,32892,32893],{},"Here’s what’s powerful about this approach:",[70,32895,32896,32899,32902,32905],{},[73,32897,32898],{},"It caught a real bug I hadn’t seen.",[73,32900,32901],{},"It saved me time writing boilerplate.",[73,32903,32904],{},"It provided test coverage ideas based on actual usage paths.",[73,32906,32907],{},"It produced runnable code I can commit right away or extend into more tests.",[11,32909,32910],{},"You can iterate, refine the prompt, increase test count, or tell the agent to explore different areas. It’s like pairing with an AI-powered tester that never gets tired.",[11,32912,32913],{},"🚀 Try It Yourself\nIf you're building modern apps and want better test coverage without writing everything by hand, this is your sign to give the Playwright MCP a try.",[11,32915,32916],{},"Just point it at your app, give it a prompt, and let it explore.\nYou’ll be surprised what it finds — and how quickly you can go from zero tests to real coverage. Test out different models and see what works best for you. For this demo I used Claude Sonnet 3.7.",[11,32918,32919,32920],{},"Check out the (video demo)",[1736,32921,32922],{},[15,32923,32924],{"href":32924,"rel":32925},"https://youtu.be/IixdI2bTR1g",[19],[11,32927,32928],{},"🧪 Happy testing — and let the bots write your tests. Let me know what you think in the comments and if you tried it out on your site and had some success. It may do things a little different depending on the model and version etc.",[11,32930,32931,32932,32934,32935,32937],{},"Tip: In my ",[179,32933,31992],{}," folder in a file called ",[179,32936,2006],{}," I add this line of code so I don't have to click continue each time. It's great for demos.",[299,32939,32941],{"className":1903,"code":32940,"language":1905,"meta":307,"style":307},"{\n    \"chat.tools.autoApprove\": true\n}\n",[179,32942,32943,32947,32956],{"__ignoreMap":307},[1736,32944,32945],{"class":1738,"line":1739},[1736,32946,1913],{"class":1912},[1736,32948,32949,32952,32954],{"class":1738,"line":748},[1736,32950,32951],{"class":1918},"    \"chat.tools.autoApprove\"",[1736,32953,3065],{"class":1912},[1736,32955,21380],{"class":1918},[1736,32957,32958],{"class":1738,"line":756},[1736,32959,1976],{"class":1912},[731,32961],{},[11,32963,32964],{},[133,32965,25534,32966,891],{},[15,32967,25539],{"href":32968,"rel":32969},"https://dev.to/debs_obrien/letting-playwright-mcp-explore-your-site-and-write-your-tests-mf1",[19],[2011,32971,32972],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":32974},[],"Learn how to use Playwright MCP (Model Context Protocol) to explore your website and automatically generate comprehensive tests, making web testing more efficient and thorough.",{"loading":3458},"/blog/letting-playwright-mcp-explore-your-site",{"title":31975,"description":32975},"blog/letting-playwright-mcp-explore-your-site",[1412,1411,3321,795],"WLVeUd2en93H2h6yTVulHbiJvoZEdRsG-xhd4mRJiIE",{"id":32983,"title":32984,"body":32985,"canonical":788,"date":32989,"description":32990,"extension":786,"featured":787,"image":32991,"meta":32992,"navigation":790,"ogimage":788,"path":32993,"provider":3460,"published":787,"seo":32994,"stem":32995,"tags":32996,"url":32997,"__hash__":32998},"blog/blog/locate-elements-playwright.md","How To Locate Elements in Playwright",{"type":8,"value":32986,"toc":32987},[],{"title":307,"searchDepth":748,"depth":748,"links":32988},[],"2022-12-01","In order to write end to end tests we need to first find elements on the webpage and then perform user actions on them. For example, find a link and click on it. But before we dive into how to use the test generator, first let's understand what a link element is and what exactly locators are.","v1630862642/debbie.codes/featured-posts/locators_jip380",{"platform":5508},"/blog/locate-elements-playwright",{"title":32984,"description":32990},"blog/locate-elements-playwright",[1412,1411],"https://dev.to/playwright/how-to-locate-elements-in-playwright-2918","8L1-vu6qPOWr-2xDrVGTDQA1x13RkEpPN4edjEcf4CE",{"id":33000,"title":33001,"body":33002,"canonical":788,"date":33006,"description":33007,"extension":786,"featured":787,"image":33008,"meta":33009,"navigation":790,"ogimage":788,"path":33011,"provider":3460,"published":787,"seo":33012,"stem":33013,"tags":33014,"url":33015,"__hash__":33016},"blog/blog/meet-debbie-8-months-to-frontend-architect.md","Meet Debbie, It Took Her Only 8 Months to Become a Front-End Architect",{"type":8,"value":33003,"toc":33004},[],{"title":307,"searchDepth":748,"depth":748,"links":33005},[],"2017-12-27","Each day we look forward to seeing our students around the world succeed as they grow and navigate within their chosen career path. We’re even more thrilled when our students become graduates and get a job in their dream role!","v1607270253/debbie.codes/blog/debbie-oc_qtvsnj",{"platform":33010},"OpenClassrooms","/blog/meet-debbie-8-months-to-frontend-architect",{"title":33001,"description":33007},"blog/meet-debbie-8-months-to-frontend-architect",[3464],"https://blog.openclassrooms.com/en/2017/12/27/success-story-8-months-front-end-architect-bluekiri/","oVd8difqtdJnvbO8_r-lknXYhpsBoTWO_9LmIA6PmWE",{"id":33018,"title":33019,"body":33020,"canonical":788,"date":36394,"description":36395,"extension":786,"featured":787,"image":36396,"meta":36397,"navigation":790,"ogimage":788,"path":36400,"provider":3460,"published":790,"seo":36401,"stem":36402,"tags":36403,"url":788,"__hash__":36404},"blog/blog/migrating-nuxt2-nuxt3.md","Migrating from Nuxt 2 to Nuxt 3",{"type":8,"value":33021,"toc":36362},[33022,33030,33033,33037,33040,33057,33060,33072,33086,33090,33093,33106,33152,33156,33159,33193,33208,33212,33219,33223,33231,33247,33253,33304,33326,33329,33394,33401,33435,33452,33582,33602,33820,33826,33885,33889,33899,33909,33913,33932,33948,33960,34017,34020,34024,34037,34053,34063,34169,34173,34176,34179,34183,34186,34200,34212,34341,34345,34358,34419,34423,34430,34446,34452,34500,34513,34567,34570,34574,34582,34598,34601,34628,34634,34682,34688,34777,34781,34784,34800,34820,34922,34929,34988,34992,35002,35029,35032,35035,35079,35086,35124,35128,35139,35237,35244,35297,35301,35304,35397,35407,35484,35496,35500,35518,35671,35674,35677,35686,35716,35720,35727,35762,35765,35826,35830,35840,35999,36002,36128,36132,36146,36175,36178,36239,36241,36247,36250,36253,36258,36262,36288,36297,36301,36314,36321,36329,36331,36359],[11,33023,33024,33025,891],{},"My personal website was built many years ago and had collected quite a large amount of code as I used my site to play around and experiment new features of Nuxt. It took me ages to finally decide to migrate cause lets face it, we all hate migrations. But I finally did it and I'm so glad I did. I'm going to share with you the steps I took to migrate my site from Nuxt 2 to Nuxt 3. My website is open source so feel free to ",[15,33026,33029],{"href":33027,"rel":33028},"https://github.com/debs-obrien/debbie.codes",[19],"check out my repo or clone it",[11,33031,33032],{},"When I first stared the migration I decided to update my package.json and install Nuxt 3 and try to fix error by error. But this didn't work out too well. There were too many and I couldn't figure out which module was causing the error or if it was something else. So I decided to start from scratch and copy over my code. This worked out much better.",[23,33034,33036],{"id":33035},"starting-from-scratch","Starting from Scratch",[11,33038,33039],{},"Now starting from scratch sounds very scary indeed. Like a lot of work and time but actually it wasn't and it proved to be the better method for migration. I created a new branch from the main branch so it included all my code. I then created a folder called \"__nuxt2\" and copied all my code into it. I now had no files or folders at root level except the nuxt2 folder. I then started a new Nuxt project following the instructions from the docs. This gave me a very basic Nuxt 3 project and somewhere to start from.",[299,33041,33043],{"className":2665,"code":33042,"language":2667,"meta":307,"style":307},"npx nuxi init nuxt-app\n",[179,33044,33045],{"__ignoreMap":307},[1736,33046,33047,33049,33052,33054],{"class":1738,"line":1739},[1736,33048,2675],{"class":2674},[1736,33050,33051],{"class":1935}," nuxi",[1736,33053,15504],{"class":1935},[1736,33055,33056],{"class":1935}," nuxt-app\n",[11,33058,33059],{},"This command creates a minimal Nuxt app inside a folder called nuxt-app. I then just moved these to root level and deleted the folder. Then I installed the dependencies and started the dev server.",[299,33061,33063],{"className":2665,"code":33062,"language":2667,"meta":307,"style":307},"npm install\n",[179,33064,33065],{"__ignoreMap":307},[1736,33066,33067,33069],{"class":1738,"line":1739},[1736,33068,6565],{"class":2674},[1736,33070,33071],{"class":1935}," install\n",[299,33073,33075],{"className":2665,"code":33074,"language":2667,"meta":307,"style":307},"npm run dev\n",[179,33076,33077],{"__ignoreMap":307},[1736,33078,33079,33081,33083],{"class":1738,"line":1739},[1736,33080,6565],{"class":2674},[1736,33082,16130],{"class":1935},[1736,33084,33085],{"class":1935}," dev\n",[23,33087,33089],{"id":33088},"whats-different","What's different?",[11,33091,33092],{},"I took a look at what had been installed to understand the differences between Nuxt 2 and Nuxt 3. Nuxt 3 is much more stripped back than Nuxt 2 with the idea of adding the folders you need rather than having everything and activating them by putting a file inside. This means by default Nuxt 3 is much smaller and doesn't include the router by default. That also means there is no pages folder by default. By adding the pages folder and putting a file inside it will create the router for you. If you only have one page, for example a landing page then you may not need a pages folder.",[11,33094,2506,33095,33098,33099,33102,33103,12452],{},[179,33096,33097],{},"app.vue"," file is the root of your application and Nuxt renders its contents for every route of the application. You will notice this page comes with a Welcome component that is built into Nuxt meaning you won't find it in the components folder. The first thing I did was remove this component and add my name in a ",[179,33100,33101],{},"\u003Ch1>"," tag. Once I saw this worked I now knew I needed to add a way to render each page. This is done with the built in ",[179,33104,33105],{},"\u003CNuxtPage />",[299,33107,33109],{"className":21974,"code":33108,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003CNuxtPage />\n  \u003C/div>\n\u003C/template>\n",[179,33110,33111,33119,33127,33136,33144],{"__ignoreMap":307},[1736,33112,33113,33115,33117],{"class":1738,"line":1739},[1736,33114,6657],{"class":1912},[1736,33116,21985],{"class":6696},[1736,33118,6663],{"class":1912},[1736,33120,33121,33123,33125],{"class":1738,"line":748},[1736,33122,7020],{"class":1912},[1736,33124,6697],{"class":6696},[1736,33126,6663],{"class":1912},[1736,33128,33129,33131,33134],{"class":1738,"line":756},[1736,33130,6693],{"class":1912},[1736,33132,33133],{"class":30490},"NuxtPage",[1736,33135,6739],{"class":1912},[1736,33137,33138,33140,33142],{"class":1738,"line":1755},[1736,33139,8096],{"class":1912},[1736,33141,6697],{"class":6696},[1736,33143,6663],{"class":1912},[1736,33145,33146,33148,33150],{"class":1738,"line":1761},[1736,33147,8105],{"class":1912},[1736,33149,21985],{"class":6696},[1736,33151,6663],{"class":1912},[23,33153,33155],{"id":33154},"adding-the-pages-folder","Adding the pages folder",[11,33157,33158],{},"Now in order to see something rendered I needed to create a pages folder and a file called index.vue. I then added the following code to the file. Inside this file I could add my name and test out that it worked.",[299,33160,33162],{"className":21974,"code":33161,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Ch1>Debbie\u003C/h1>\n\u003C/template>\n",[179,33163,33164,33172,33185],{"__ignoreMap":307},[1736,33165,33166,33168,33170],{"class":1738,"line":1739},[1736,33167,6657],{"class":1912},[1736,33169,21985],{"class":6696},[1736,33171,6663],{"class":1912},[1736,33173,33174,33176,33178,33181,33183],{"class":1738,"line":748},[1736,33175,7020],{"class":1912},[1736,33177,30550],{"class":6696},[1736,33179,33180],{"class":1912},">Debbie\u003C/",[1736,33182,30550],{"class":6696},[1736,33184,6663],{"class":1912},[1736,33186,33187,33189,33191],{"class":1738,"line":756},[1736,33188,8105],{"class":1912},[1736,33190,21985],{"class":6696},[1736,33192,6663],{"class":1912},[11,33194,33195,33196,33199,33200,33203,33204,33207],{},"And it worked! I now had my name rendered on the page. I then added a second page called ",[179,33197,33198],{},"about.vue"," and added some text. I checked routing was working by adding 'about' at the end of ",[179,33201,33202],{},"http://localhost:3000/about"," in the browser url. This has always been one of my favourite features of Nuxt. No need to setup or understand how routing works. You just have to add ",[179,33205,33206],{},".vue"," files to the pages folder and thats it.",[23,33209,33211],{"id":33210},"adding-content","Adding content",[11,33213,33214,33215,33218],{},"I decided to focus on adding all content to the index page as this page contained many components and also was using Nuxt content to fetch data from my markdown files. First thing I did was copy the contents of my index file from the Nuxt 2 folder into my new index file. I simplified things by commenting out anything in the ",[179,33216,33217],{},"\u003Cscript>"," tag and any components that relied on the Nuxt Content Module such as the blog post cards and video cards etc. This left me with a very basic page with just text rendered with no formatting and broken images.",[23,33220,33222],{"id":33221},"nuxt-content-module","Nuxt Content Module",[11,33224,33225,33226,891],{},"I decided to work on getting content rendered first. All my content comes from markdown files which are located in a content folder. The Nuxt content module works like a git based CMS making it really easy to write blog posts in markdown or use yaml to create content for video or podcast cards and easily render them throughout the site. The module has a great feature for querying so you can get back only what you need just like if it were stored in a database. I started by copying the content folder from my nuxt 2 folder and pasted it at root level of. I then ",[15,33227,33230],{"href":33228,"rel":33229},"https://content.nuxtjs.org/get-started#add-to-a-project",[19],"installed the Nuxt Content Module",[299,33232,33234],{"className":2665,"code":33233,"language":2667,"meta":307,"style":307},"npm install -D @nuxt/content\n",[179,33235,33236],{"__ignoreMap":307},[1736,33237,33238,33240,33242,33244],{"class":1738,"line":1739},[1736,33239,6565],{"class":2674},[1736,33241,4973],{"class":1935},[1736,33243,21717],{"class":1918},[1736,33245,33246],{"class":1935}," @nuxt/content\n",[11,33248,33249,33250,2900],{},"I then added it to the modules array in the ",[179,33251,33252],{},"nuxt.config.js",[299,33254,33256],{"className":8734,"code":33255,"language":8736,"meta":307,"style":307},"import { defineNuxtConfig } from 'nuxt'\n\nexport default defineNuxtConfig({\n  modules: [\n    '@nuxt/content'\n  ],\n})\n",[179,33257,33258,33270,33274,33285,33290,33295,33300],{"__ignoreMap":307},[1736,33259,33260,33262,33265,33267],{"class":1738,"line":1739},[1736,33261,4996],{"class":4866},[1736,33263,33264],{"class":1912}," { defineNuxtConfig } ",[1736,33266,5002],{"class":4866},[1736,33268,33269],{"class":1935}," 'nuxt'\n",[1736,33271,33272],{"class":1738,"line":748},[1736,33273,1747],{"emptyLinePlaceholder":790},[1736,33275,33276,33278,33280,33283],{"class":1738,"line":756},[1736,33277,6632],{"class":4866},[1736,33279,30438],{"class":4866},[1736,33281,33282],{"class":2674}," defineNuxtConfig",[1736,33284,5122],{"class":1912},[1736,33286,33287],{"class":1738,"line":1755},[1736,33288,33289],{"class":1912},"  modules: [\n",[1736,33291,33292],{"class":1738,"line":1761},[1736,33293,33294],{"class":1935},"    '@nuxt/content'\n",[1736,33296,33297],{"class":1738,"line":1767},[1736,33298,33299],{"class":1912},"  ],\n",[1736,33301,33302],{"class":1738,"line":1772},[1736,33303,10582],{"class":1912},[11,33305,33306,33307,608,33310,33313,33314,33317,33318,33323,33324,891],{},"Next step was to query some content. There are a few differences between Nuxt content version 1 and version 2 when it comes to querying data. Fetch has changed to ",[179,33308,33309],{},"find()",[179,33311,33312],{},"queryContent"," composable instead of the ",[179,33315,33316],{},"$content"," variable. I suggest taking a look at the ",[15,33319,33322],{"href":33320,"rel":33321},"https://content.nuxtjs.org/guide/displaying/querying",[19],"Nuxt Content docs"," to see examples of using ",[179,33325,33312],{},[11,33327,33328],{},"I stared out with a stripped back version of my query in order to just see some data rendered. In my content folder I previously had a folder called articles which I renamed to blog as blog is used as the root path.",[299,33330,33332],{"className":8734,"code":33331,"language":8736,"meta":307,"style":307},"const { data: articles } = await useAsyncData('articles',\n  () => queryContent('blog')\n    .find()\n)\n",[179,33333,33334,33364,33381,33390],{"__ignoreMap":307},[1736,33335,33336,33338,33340,33343,33345,33348,33350,33352,33354,33357,33359,33362],{"class":1738,"line":1739},[1736,33337,5029],{"class":4866},[1736,33339,7827],{"class":1912},[1736,33341,33342],{"class":5036},"data",[1736,33344,3065],{"class":1912},[1736,33346,33347],{"class":1918},"articles",[1736,33349,7832],{"class":1912},[1736,33351,5062],{"class":4866},[1736,33353,18389],{"class":4866},[1736,33355,33356],{"class":2674}," useAsyncData",[1736,33358,7751],{"class":1912},[1736,33360,33361],{"class":1935},"'articles'",[1736,33363,1939],{"class":1912},[1736,33365,33366,33369,33371,33374,33376,33379],{"class":1738,"line":748},[1736,33367,33368],{"class":1912},"  () ",[1736,33370,7013],{"class":4866},[1736,33372,33373],{"class":2674}," queryContent",[1736,33375,7751],{"class":1912},[1736,33377,33378],{"class":1935},"'blog'",[1736,33380,7045],{"class":1912},[1736,33382,33383,33386,33388],{"class":1738,"line":756},[1736,33384,33385],{"class":1912},"    .",[1736,33387,7770],{"class":2674},[1736,33389,22680],{"class":1912},[1736,33391,33392],{"class":1738,"line":1755},[1736,33393,7045],{"class":1912},[11,33395,33396,33397,33400],{},"Then in the template I added a ",[179,33398,33399],{},"\u003Cpre>"," tag to render the data.",[299,33402,33404],{"className":21974,"code":33403,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cpre>{{ articles }}\u003C/pre>\n\u003C/template>\n",[179,33405,33406,33414,33427],{"__ignoreMap":307},[1736,33407,33408,33410,33412],{"class":1738,"line":1739},[1736,33409,6657],{"class":1912},[1736,33411,21985],{"class":6696},[1736,33413,6663],{"class":1912},[1736,33415,33416,33418,33420,33423,33425],{"class":1738,"line":748},[1736,33417,7020],{"class":1912},[1736,33419,299],{"class":6696},[1736,33421,33422],{"class":1912},">{{ articles }}\u003C/",[1736,33424,299],{"class":6696},[1736,33426,6663],{"class":1912},[1736,33428,33429,33431,33433],{"class":1738,"line":756},[1736,33430,8105],{"class":1912},[1736,33432,21985],{"class":6696},[1736,33434,6663],{"class":1912},[11,33436,33437,33438,33441,33442,33444,33445,33448,33449,9384],{},"Once I saw I had some data back I then improved the query to only get back the data I needed. The ",[179,33439,33440],{},"sort()"," method is a little bit different than before and the ",[179,33443,33309],{}," method has changed to a ",[179,33446,33447],{},"fetch()"," method but the rest was the same. Also make sure you add a key as the first argument to the ",[179,33450,33451],{},"useAsyncData()",[299,33453,33455],{"className":8734,"code":33454,"language":8736,"meta":307,"style":307},"const { data: articles } = await useAsyncData('articles',\n  () => queryContent('blog')\n    .where({ published: { $ne: false } })\n    .without('body')\n    .skip(1)\n    .sort({ date: -1 })\n    .limit(6)\n    .find()\n)\n",[179,33456,33457,33483,33497,33513,33527,33540,33556,33570,33578],{"__ignoreMap":307},[1736,33458,33459,33461,33463,33465,33467,33469,33471,33473,33475,33477,33479,33481],{"class":1738,"line":1739},[1736,33460,5029],{"class":4866},[1736,33462,7827],{"class":1912},[1736,33464,33342],{"class":5036},[1736,33466,3065],{"class":1912},[1736,33468,33347],{"class":1918},[1736,33470,7832],{"class":1912},[1736,33472,5062],{"class":4866},[1736,33474,18389],{"class":4866},[1736,33476,33356],{"class":2674},[1736,33478,7751],{"class":1912},[1736,33480,33361],{"class":1935},[1736,33482,1939],{"class":1912},[1736,33484,33485,33487,33489,33491,33493,33495],{"class":1738,"line":748},[1736,33486,33368],{"class":1912},[1736,33488,7013],{"class":4866},[1736,33490,33373],{"class":2674},[1736,33492,7751],{"class":1912},[1736,33494,33378],{"class":1935},[1736,33496,7045],{"class":1912},[1736,33498,33499,33501,33504,33507,33510],{"class":1738,"line":756},[1736,33500,33385],{"class":1912},[1736,33502,33503],{"class":2674},"where",[1736,33505,33506],{"class":1912},"({ published: { $ne: ",[1736,33508,33509],{"class":1918},"false",[1736,33511,33512],{"class":1912}," } })\n",[1736,33514,33515,33517,33520,33522,33525],{"class":1738,"line":1755},[1736,33516,33385],{"class":1912},[1736,33518,33519],{"class":2674},"without",[1736,33521,7751],{"class":1912},[1736,33523,33524],{"class":1935},"'body'",[1736,33526,7045],{"class":1912},[1736,33528,33529,33531,33534,33536,33538],{"class":1738,"line":1761},[1736,33530,33385],{"class":1912},[1736,33532,33533],{"class":2674},"skip",[1736,33535,7751],{"class":1912},[1736,33537,10249],{"class":1918},[1736,33539,7045],{"class":1912},[1736,33541,33542,33544,33547,33550,33552,33554],{"class":1738,"line":1767},[1736,33543,33385],{"class":1912},[1736,33545,33546],{"class":2674},"sort",[1736,33548,33549],{"class":1912},"({ date: ",[1736,33551,9419],{"class":4866},[1736,33553,10249],{"class":1918},[1736,33555,10691],{"class":1912},[1736,33557,33558,33560,33563,33565,33568],{"class":1738,"line":1772},[1736,33559,33385],{"class":1912},[1736,33561,33562],{"class":2674},"limit",[1736,33564,7751],{"class":1912},[1736,33566,33567],{"class":1918},"6",[1736,33569,7045],{"class":1912},[1736,33571,33572,33574,33576],{"class":1738,"line":1778},[1736,33573,33385],{"class":1912},[1736,33575,7770],{"class":2674},[1736,33577,22680],{"class":1912},[1736,33579,33580],{"class":1738,"line":1784},[1736,33581,7045],{"class":1912},[11,33583,33584,33585,33588,33589,33591,33592,608,33595,3733,33597,33599,33600,10763],{},"I now had just the data from the ",[179,33586,33587],{},"yaml"," part of my markdown files so I uncommented out the rest of my queries changing the ",[179,33590,33316],{}," variable to ",[179,33593,33594],{},"queryContent()",[179,33596,33447],{},[179,33598,33309],{}," and refactoring the ",[179,33601,33440],{},[299,33603,33605],{"className":8734,"code":33604,"language":8736,"meta":307,"style":307},"const { data: videos } = await useAsyncData('videos',\n  () => queryContent('videos')\n    .where({ published: { $ne: false } })\n    .without('body')\n    .sort({ date: -1 })\n    .limit(4)\n    .find()\n)\n\nconst { data: podcasts } = await useAsyncData('podcasts',\n  () => queryContent('podcasts')\n    .where({ published: { $ne: false } })\n    .without('body')\n    .sort({ date: -1 })\n    .limit(3)\n    .find()\n)\n",[179,33606,33607,33635,33649,33661,33673,33687,33700,33708,33712,33716,33744,33758,33770,33782,33796,33808,33816],{"__ignoreMap":307},[1736,33608,33609,33611,33613,33615,33617,33620,33622,33624,33626,33628,33630,33633],{"class":1738,"line":1739},[1736,33610,5029],{"class":4866},[1736,33612,7827],{"class":1912},[1736,33614,33342],{"class":5036},[1736,33616,3065],{"class":1912},[1736,33618,33619],{"class":1918},"videos",[1736,33621,7832],{"class":1912},[1736,33623,5062],{"class":4866},[1736,33625,18389],{"class":4866},[1736,33627,33356],{"class":2674},[1736,33629,7751],{"class":1912},[1736,33631,33632],{"class":1935},"'videos'",[1736,33634,1939],{"class":1912},[1736,33636,33637,33639,33641,33643,33645,33647],{"class":1738,"line":748},[1736,33638,33368],{"class":1912},[1736,33640,7013],{"class":4866},[1736,33642,33373],{"class":2674},[1736,33644,7751],{"class":1912},[1736,33646,33632],{"class":1935},[1736,33648,7045],{"class":1912},[1736,33650,33651,33653,33655,33657,33659],{"class":1738,"line":756},[1736,33652,33385],{"class":1912},[1736,33654,33503],{"class":2674},[1736,33656,33506],{"class":1912},[1736,33658,33509],{"class":1918},[1736,33660,33512],{"class":1912},[1736,33662,33663,33665,33667,33669,33671],{"class":1738,"line":1755},[1736,33664,33385],{"class":1912},[1736,33666,33519],{"class":2674},[1736,33668,7751],{"class":1912},[1736,33670,33524],{"class":1935},[1736,33672,7045],{"class":1912},[1736,33674,33675,33677,33679,33681,33683,33685],{"class":1738,"line":1761},[1736,33676,33385],{"class":1912},[1736,33678,33546],{"class":2674},[1736,33680,33549],{"class":1912},[1736,33682,9419],{"class":4866},[1736,33684,10249],{"class":1918},[1736,33686,10691],{"class":1912},[1736,33688,33689,33691,33693,33695,33698],{"class":1738,"line":1767},[1736,33690,33385],{"class":1912},[1736,33692,33562],{"class":2674},[1736,33694,7751],{"class":1912},[1736,33696,33697],{"class":1918},"4",[1736,33699,7045],{"class":1912},[1736,33701,33702,33704,33706],{"class":1738,"line":1772},[1736,33703,33385],{"class":1912},[1736,33705,7770],{"class":2674},[1736,33707,22680],{"class":1912},[1736,33709,33710],{"class":1738,"line":1778},[1736,33711,7045],{"class":1912},[1736,33713,33714],{"class":1738,"line":1784},[1736,33715,1747],{"emptyLinePlaceholder":790},[1736,33717,33718,33720,33722,33724,33726,33729,33731,33733,33735,33737,33739,33742],{"class":1738,"line":1790},[1736,33719,5029],{"class":4866},[1736,33721,7827],{"class":1912},[1736,33723,33342],{"class":5036},[1736,33725,3065],{"class":1912},[1736,33727,33728],{"class":1918},"podcasts",[1736,33730,7832],{"class":1912},[1736,33732,5062],{"class":4866},[1736,33734,18389],{"class":4866},[1736,33736,33356],{"class":2674},[1736,33738,7751],{"class":1912},[1736,33740,33741],{"class":1935},"'podcasts'",[1736,33743,1939],{"class":1912},[1736,33745,33746,33748,33750,33752,33754,33756],{"class":1738,"line":1796},[1736,33747,33368],{"class":1912},[1736,33749,7013],{"class":4866},[1736,33751,33373],{"class":2674},[1736,33753,7751],{"class":1912},[1736,33755,33741],{"class":1935},[1736,33757,7045],{"class":1912},[1736,33759,33760,33762,33764,33766,33768],{"class":1738,"line":2353},[1736,33761,33385],{"class":1912},[1736,33763,33503],{"class":2674},[1736,33765,33506],{"class":1912},[1736,33767,33509],{"class":1918},[1736,33769,33512],{"class":1912},[1736,33771,33772,33774,33776,33778,33780],{"class":1738,"line":2358},[1736,33773,33385],{"class":1912},[1736,33775,33519],{"class":2674},[1736,33777,7751],{"class":1912},[1736,33779,33524],{"class":1935},[1736,33781,7045],{"class":1912},[1736,33783,33784,33786,33788,33790,33792,33794],{"class":1738,"line":2364},[1736,33785,33385],{"class":1912},[1736,33787,33546],{"class":2674},[1736,33789,33549],{"class":1912},[1736,33791,9419],{"class":4866},[1736,33793,10249],{"class":1918},[1736,33795,10691],{"class":1912},[1736,33797,33798,33800,33802,33804,33806],{"class":1738,"line":2370},[1736,33799,33385],{"class":1912},[1736,33801,33562],{"class":2674},[1736,33803,7751],{"class":1912},[1736,33805,1265],{"class":1918},[1736,33807,7045],{"class":1912},[1736,33809,33810,33812,33814],{"class":1738,"line":2376},[1736,33811,33385],{"class":1912},[1736,33813,7770],{"class":2674},[1736,33815,22680],{"class":1912},[1736,33817,33818],{"class":1738,"line":2381},[1736,33819,7045],{"class":1912},[11,33821,33822,33823,33825],{},"I then added a ",[179,33824,33399],{}," tag to render each query.",[299,33827,33829],{"className":21974,"code":33828,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cpre>{{ articles }}\u003C/pre>\n  \u003Cpre>{{ podcasts }}\u003C/pre>\n  \u003Cpre>{{ videos }}\u003C/pre>\n\u003C/template>\n",[179,33830,33831,33839,33851,33864,33877],{"__ignoreMap":307},[1736,33832,33833,33835,33837],{"class":1738,"line":1739},[1736,33834,6657],{"class":1912},[1736,33836,21985],{"class":6696},[1736,33838,6663],{"class":1912},[1736,33840,33841,33843,33845,33847,33849],{"class":1738,"line":748},[1736,33842,7020],{"class":1912},[1736,33844,299],{"class":6696},[1736,33846,33422],{"class":1912},[1736,33848,299],{"class":6696},[1736,33850,6663],{"class":1912},[1736,33852,33853,33855,33857,33860,33862],{"class":1738,"line":756},[1736,33854,7020],{"class":1912},[1736,33856,299],{"class":6696},[1736,33858,33859],{"class":1912},">{{ podcasts }}\u003C/",[1736,33861,299],{"class":6696},[1736,33863,6663],{"class":1912},[1736,33865,33866,33868,33870,33873,33875],{"class":1738,"line":1755},[1736,33867,7020],{"class":1912},[1736,33869,299],{"class":6696},[1736,33871,33872],{"class":1912},">{{ videos }}\u003C/",[1736,33874,299],{"class":6696},[1736,33876,6663],{"class":1912},[1736,33878,33879,33881,33883],{"class":1738,"line":1761},[1736,33880,8105],{"class":1912},[1736,33882,21985],{"class":6696},[1736,33884,6663],{"class":1912},[23,33886,33888],{"id":33887},"adding-components","Adding components",[11,33890,33891,33892,33895,33896,33898],{},"Next step was to add the components so that they could be rendered correctly. I copied over only the components I needed from my Nuxt 2 folder starting with the BlogPostCard component, VideoCard component and PodcastCard component. I then uncommented out the components from the ",[179,33893,33894],{},"index.vue"," file and removed the ",[179,33897,33399],{}," tags. Things now looked a little tidier on the page but were still super ugly as I had no styling added yet. I also had some title components which I copied to the components folder and then uncommented out the code so they could also be rendered.",[1713,33900,33901],{},[11,33902,33903,33904,891],{},"The great thing about Nuxt 3 is that once I add a component to the components folder I can just use it directly in any of my pages without having to import it or add any module. It just works thanks to ",[15,33905,33908],{"href":33906,"rel":33907},"https://nuxt.com/docs/guide/concepts/auto-imports",[19],"Nuxt's auto imports",[23,33910,33912],{"id":33911},"adding-styling","Adding styling",[11,33914,33915,33916,33919,33920,33923,33924,33926,33927,891],{},"I now had a super ugly page with all the content rendered but it was time to add some styling. I first created an ",[179,33917,33918],{},"assets"," folder and then copied over the ",[179,33921,33922],{},"main.css"," file from my Nuxt 2 folder. I then copied over my ",[179,33925,4890],{}," file and installed ",[15,33928,33931],{"href":33929,"rel":33930},"https://tailwindcss.com/",[19],"Tailwind",[299,33933,33935],{"className":2665,"code":33934,"language":2667,"meta":307,"style":307},"npm i -D @nuxtjs/tailwindcss\n",[179,33936,33937],{"__ignoreMap":307},[1736,33938,33939,33941,33943,33945],{"class":1738,"line":1739},[1736,33940,6565],{"class":2674},[1736,33942,6568],{"class":1935},[1736,33944,21717],{"class":1918},[1736,33946,33947],{"class":1935}," @nuxtjs/tailwindcss\n",[11,33949,33950,33951,33954,33955,33957,33958,2900],{},"Next I added the ",[179,33952,33953],{},"@nuxtjs/tailwindcss"," module to the ",[179,33956,33252],{}," file as well as the css array with the path to my ",[179,33959,33922],{},[299,33961,33963],{"className":8734,"code":33962,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n  modules: [\n    '@nuxtjs/tailwindcss',\n    '@nuxt/content',\n  ],\n  css: [\n    '~/assets/css/main.css',\n  ],\n})\n",[179,33964,33965,33975,33979,33986,33993,33997,34002,34009,34013],{"__ignoreMap":307},[1736,33966,33967,33969,33971,33973],{"class":1738,"line":1739},[1736,33968,6632],{"class":4866},[1736,33970,30438],{"class":4866},[1736,33972,33282],{"class":2674},[1736,33974,5122],{"class":1912},[1736,33976,33977],{"class":1738,"line":748},[1736,33978,33289],{"class":1912},[1736,33980,33981,33984],{"class":1738,"line":756},[1736,33982,33983],{"class":1935},"    '@nuxtjs/tailwindcss'",[1736,33985,1939],{"class":1912},[1736,33987,33988,33991],{"class":1738,"line":1755},[1736,33989,33990],{"class":1935},"    '@nuxt/content'",[1736,33992,1939],{"class":1912},[1736,33994,33995],{"class":1738,"line":1761},[1736,33996,33299],{"class":1912},[1736,33998,33999],{"class":1738,"line":1767},[1736,34000,34001],{"class":1912},"  css: [\n",[1736,34003,34004,34007],{"class":1738,"line":1772},[1736,34005,34006],{"class":1935},"    '~/assets/css/main.css'",[1736,34008,1939],{"class":1912},[1736,34010,34011],{"class":1738,"line":1778},[1736,34012,33299],{"class":1912},[1736,34014,34015],{"class":1738,"line":1784},[1736,34016,10582],{"class":1912},[11,34018,34019],{},"I now had all styles working and the home page was looking pretty good indeed.",[23,34021,34023],{"id":34022},"nuxt-image-module","Nuxt Image Module",[11,34025,34026,34027,34031,34032,34036],{},"In order to render the images I had previously been using the ",[15,34028,34023],{"href":34029,"rel":34030},"https://image.nuxtjs.org/",[19]," for ",[15,34033,34035],{"href":34034},"/blog/nuxt-image","enhanced performance"," so I needed to install it. This module is still on the edge version but it seems to work just fine.",[299,34038,34040],{"className":2665,"code":34039,"language":2667,"meta":307,"style":307},"npm i -D @nuxt/image-edge\n",[179,34041,34042],{"__ignoreMap":307},[1736,34043,34044,34046,34048,34050],{"class":1738,"line":1739},[1736,34045,6565],{"class":2674},[1736,34047,6568],{"class":1935},[1736,34049,21717],{"class":1918},[1736,34051,34052],{"class":1935}," @nuxt/image-edge\n",[11,34054,34055,34056,34058,34059,34062],{},"Once installed I added it to the modules array in the ",[179,34057,33252],{}," file and copied my configuration for the module from my ",[179,34060,34061],{},"nuxt.config"," file in my Nuxt 2 folder. Ths included adding my cloudinary baseURL and imgix baseURL as these were the two cloud providers I was using.",[299,34064,34066],{"className":8734,"code":34065,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n  modules: [\n    '@nuxt/image-edge',\n    '@nuxtjs/tailwindcss',\n    '@nuxt/content',\n  ],\n  css: [\n    '~/assets/css/main.css',\n  ],\n  image: {\n    cloudinary: {\n      baseURL: 'https://res.cloudinary.com/debsobrien/image/upload/',\n    },\n    imgix: {\n      baseURL: 'https://images.unsplash.com/',\n    },\n  },\n})\n",[179,34067,34068,34078,34082,34089,34095,34101,34105,34109,34115,34119,34124,34129,34139,34143,34148,34157,34161,34165],{"__ignoreMap":307},[1736,34069,34070,34072,34074,34076],{"class":1738,"line":1739},[1736,34071,6632],{"class":4866},[1736,34073,30438],{"class":4866},[1736,34075,33282],{"class":2674},[1736,34077,5122],{"class":1912},[1736,34079,34080],{"class":1738,"line":748},[1736,34081,33289],{"class":1912},[1736,34083,34084,34087],{"class":1738,"line":756},[1736,34085,34086],{"class":1935},"    '@nuxt/image-edge'",[1736,34088,1939],{"class":1912},[1736,34090,34091,34093],{"class":1738,"line":1755},[1736,34092,33983],{"class":1935},[1736,34094,1939],{"class":1912},[1736,34096,34097,34099],{"class":1738,"line":1761},[1736,34098,33990],{"class":1935},[1736,34100,1939],{"class":1912},[1736,34102,34103],{"class":1738,"line":1767},[1736,34104,33299],{"class":1912},[1736,34106,34107],{"class":1738,"line":1772},[1736,34108,34001],{"class":1912},[1736,34110,34111,34113],{"class":1738,"line":1778},[1736,34112,34006],{"class":1935},[1736,34114,1939],{"class":1912},[1736,34116,34117],{"class":1738,"line":1784},[1736,34118,33299],{"class":1912},[1736,34120,34121],{"class":1738,"line":1790},[1736,34122,34123],{"class":1912},"  image: {\n",[1736,34125,34126],{"class":1738,"line":1796},[1736,34127,34128],{"class":1912},"    cloudinary: {\n",[1736,34130,34131,34134,34137],{"class":1738,"line":2353},[1736,34132,34133],{"class":1912},"      baseURL: ",[1736,34135,34136],{"class":1935},"'https://res.cloudinary.com/debsobrien/image/upload/'",[1736,34138,1939],{"class":1912},[1736,34140,34141],{"class":1738,"line":2358},[1736,34142,8553],{"class":1912},[1736,34144,34145],{"class":1738,"line":2364},[1736,34146,34147],{"class":1912},"    imgix: {\n",[1736,34149,34150,34152,34155],{"class":1738,"line":2370},[1736,34151,34133],{"class":1912},[1736,34153,34154],{"class":1935},"'https://images.unsplash.com/'",[1736,34156,1939],{"class":1912},[1736,34158,34159],{"class":1738,"line":2376},[1736,34160,8553],{"class":1912},[1736,34162,34163],{"class":1738,"line":2381},[1736,34164,4929],{"class":1912},[1736,34166,34167],{"class":1738,"line":2387},[1736,34168,10582],{"class":1912},[23,34170,34172],{"id":34171},"header-and-footer","Header and Footer",[11,34174,34175],{},"I now had images rendered and a pretty good looking home page. Next step was to add the header and footer components to have a complete looking page. I started by copying the header, footer and Navigation component into my Nuxt 3 folder and then uncommenting them on the home page. As mentioned before adding components to the components folder makes them available on any page with no need to import them in your file and no module needed as it's built in to Nuxt 3.",[11,34177,34178],{},"The header component was pretty straight forward but the footer component included svgs that were using a module. I decided to not use the svg module for now and just convert my social icons into Vue components and add them to an Icon folder inside the components folder. I could then just use these icon components like any other vue component.",[23,34180,34182],{"id":34181},"pages-and-dynamic-routes","Pages and dynamic routes",[11,34184,34185],{},"The home page was pretty much complete but none of the links in the Navigation worked as I didn't have any pages yet in my pages folder except the about page I previously created. Nuxt does all the work for you when it comes to routing so the only thing you have to do is add the pages into the pages folder. I copied over the missing pages for the blog, podcasts and videos etc. As these pages all query the content folder I refactored the query to use Convent v2 just like I had done on the home page.",[11,34187,34188,34189,34192,34193,34196,34197,34199],{},"The main difference was dynamic pages. This had changed and now the dynamic page is wrapped in square brackets, ",[179,34190,34191],{},"[slug].vue"," instead of the ",[179,34194,34195],{},"_slug.vue",". I took this opportunity to refactor the way I was handing the blog categories and remove pagination in favour of adding more categories and improving the filtering. This was done by creating a ",[179,34198,34191],{}," page in a tags folder so that I could have a page for each tag such as a page for 'nuxt', 'testing' etc. I also added all videos into one content folder called videos and added categories and the tags component to all other similar pages.",[11,34201,34202,34203,34206,34207,34209,34210,9384],{},"When working with params in Nuxt 3 we have a ",[179,34204,34205],{},"useRoute()"," composeable. For the data call make sure you add a unique key to your ",[179,34208,33451],{}," so that the change in slug changes the value of the call. This gets passed in as the first argument to the ",[179,34211,33451],{},[299,34213,34215],{"className":8734,"code":34214,"language":8736,"meta":307,"style":307},"const {\n  params: { slug },\n} = useRoute()\n\nconst { data: articles } = await useAsyncData(`articles-${slug}`,\n  () => queryContent\u003CBlogPost>('blog')\n    .where({ published: { $ne: false }, tags: { $contains: slug } })\n    .sort({ date: -1 })\n    .find(),\n)\n",[179,34216,34217,34223,34235,34246,34250,34281,34301,34314,34328,34337],{"__ignoreMap":307},[1736,34218,34219,34221],{"class":1738,"line":1739},[1736,34220,5029],{"class":4866},[1736,34222,4914],{"class":1912},[1736,34224,34225,34228,34230,34233],{"class":1738,"line":748},[1736,34226,34227],{"class":5036},"  params",[1736,34229,7164],{"class":1912},[1736,34231,34232],{"class":1918},"slug",[1736,34234,7197],{"class":1912},[1736,34236,34237,34239,34241,34244],{"class":1738,"line":756},[1736,34238,6871],{"class":1912},[1736,34240,5062],{"class":4866},[1736,34242,34243],{"class":2674}," useRoute",[1736,34245,22680],{"class":1912},[1736,34247,34248],{"class":1738,"line":1755},[1736,34249,1747],{"emptyLinePlaceholder":790},[1736,34251,34252,34254,34256,34258,34260,34262,34264,34266,34268,34270,34272,34275,34277,34279],{"class":1738,"line":1761},[1736,34253,5029],{"class":4866},[1736,34255,7827],{"class":1912},[1736,34257,33342],{"class":5036},[1736,34259,3065],{"class":1912},[1736,34261,33347],{"class":1918},[1736,34263,7832],{"class":1912},[1736,34265,5062],{"class":4866},[1736,34267,18389],{"class":4866},[1736,34269,33356],{"class":2674},[1736,34271,7751],{"class":1912},[1736,34273,34274],{"class":1935},"`articles-${",[1736,34276,34232],{"class":1912},[1736,34278,16103],{"class":1935},[1736,34280,1939],{"class":1912},[1736,34282,34283,34285,34287,34289,34291,34294,34297,34299],{"class":1738,"line":1767},[1736,34284,33368],{"class":1912},[1736,34286,7013],{"class":4866},[1736,34288,33373],{"class":2674},[1736,34290,6657],{"class":1912},[1736,34292,34293],{"class":2674},"BlogPost",[1736,34295,34296],{"class":1912},">(",[1736,34298,33378],{"class":1935},[1736,34300,7045],{"class":1912},[1736,34302,34303,34305,34307,34309,34311],{"class":1738,"line":1772},[1736,34304,33385],{"class":1912},[1736,34306,33503],{"class":2674},[1736,34308,33506],{"class":1912},[1736,34310,33509],{"class":1918},[1736,34312,34313],{"class":1912}," }, tags: { $contains: slug } })\n",[1736,34315,34316,34318,34320,34322,34324,34326],{"class":1738,"line":1778},[1736,34317,33385],{"class":1912},[1736,34319,33546],{"class":2674},[1736,34321,33549],{"class":1912},[1736,34323,9419],{"class":4866},[1736,34325,10249],{"class":1918},[1736,34327,10691],{"class":1912},[1736,34329,34330,34332,34334],{"class":1738,"line":1784},[1736,34331,33385],{"class":1912},[1736,34333,7770],{"class":2674},[1736,34335,34336],{"class":1912},"(),\n",[1736,34338,34339],{"class":1738,"line":1790},[1736,34340,7045],{"class":1912},[23,34342,34344],{"id":34343},"rendering-markdown-content","Rendering markdown content",[11,34346,34347,34348,34355,34356,9252],{},"There were also some small difference when rending the blog page and there is now a new component to render the markdown content called ",[15,34349,34352],{"href":34350,"rel":34351},"https://content.nuxtjs.org/guide/displaying/rendering#contentrenderer-",[19],[179,34353,34354],{},"ContentRenderer"," passing in your data into the ",[179,34357,13315],{},[299,34359,34361],{"className":21974,"code":34360,"language":21976,"meta":307,"style":307},"\u003CContentRenderer :value=\"article\">\n  \u003Ctemplate #empty>\n    \u003Cp>No content found.\u003C/p>\n  \u003C/template>\n\u003C/ContentRenderer>\n",[179,34362,34363,34379,34390,34403,34411],{"__ignoreMap":307},[1736,34364,34365,34367,34369,34372,34374,34377],{"class":1738,"line":1739},[1736,34366,6657],{"class":1912},[1736,34368,34354],{"class":30490},[1736,34370,34371],{"class":2674}," :value",[1736,34373,5062],{"class":1912},[1736,34375,34376],{"class":1935},"\"article\"",[1736,34378,6663],{"class":1912},[1736,34380,34381,34383,34385,34388],{"class":1738,"line":748},[1736,34382,7020],{"class":1912},[1736,34384,21985],{"class":6696},[1736,34386,34387],{"class":2674}," #empty",[1736,34389,6663],{"class":1912},[1736,34391,34392,34394,34396,34399,34401],{"class":1738,"line":756},[1736,34393,6693],{"class":1912},[1736,34395,11],{"class":6696},[1736,34397,34398],{"class":1912},">No content found.\u003C/",[1736,34400,11],{"class":6696},[1736,34402,6663],{"class":1912},[1736,34404,34405,34407,34409],{"class":1738,"line":1755},[1736,34406,8096],{"class":1912},[1736,34408,21985],{"class":6696},[1736,34410,6663],{"class":1912},[1736,34412,34413,34415,34417],{"class":1738,"line":1761},[1736,34414,8105],{"class":1912},[1736,34416,34354],{"class":30490},[1736,34418,6663],{"class":1912},[23,34420,34422],{"id":34421},"styling-the-blog-page","Styling the blog page",[11,34424,34425,34426,34429],{},"To easily style the main blog article I used the ",[179,34427,34428],{},"@tailwindcss/typography"," plugin just like before. I first installed it.",[299,34431,34433],{"className":2665,"code":34432,"language":2667,"meta":307,"style":307},"npm i -D @tailwindcss/typography\n",[179,34434,34435],{"__ignoreMap":307},[1736,34436,34437,34439,34441,34443],{"class":1738,"line":1739},[1736,34438,6565],{"class":2674},[1736,34440,6568],{"class":1935},[1736,34442,21717],{"class":1918},[1736,34444,34445],{"class":1935}," @tailwindcss/typography\n",[11,34447,34448,34449,34451],{},"I already had this plugin added to my plugins array in the ",[179,34450,4890],{}," file as I had copied over the whole config file and was previously using this for styling markdown content.",[299,34453,34455],{"className":8734,"code":34454,"language":8736,"meta":307,"style":307},"module.exports = {\n  ..\n  plugins: [\n    require('@tailwindcss/typography'),\n  ],\n}\n",[179,34456,34457,34469,34474,34479,34492,34496],{"__ignoreMap":307},[1736,34458,34459,34461,34463,34465,34467],{"class":1738,"line":1739},[1736,34460,4903],{"class":1918},[1736,34462,891],{"class":1912},[1736,34464,4908],{"class":1918},[1736,34466,4911],{"class":4866},[1736,34468,4914],{"class":1912},[1736,34470,34471],{"class":1738,"line":748},[1736,34472,34473],{"class":1912},"  ..\n",[1736,34475,34476],{"class":1738,"line":756},[1736,34477,34478],{"class":1912},"  plugins: [\n",[1736,34480,34481,34484,34486,34489],{"class":1738,"line":1755},[1736,34482,34483],{"class":2674},"    require",[1736,34485,7751],{"class":1912},[1736,34487,34488],{"class":1935},"'@tailwindcss/typography'",[1736,34490,34491],{"class":1912},"),\n",[1736,34493,34494],{"class":1738,"line":1761},[1736,34495,33299],{"class":1912},[1736,34497,34498],{"class":1738,"line":1767},[1736,34499,1976],{"class":1912},[11,34501,34502,34503,34506,34507,34510,34511,2900],{},"For syntax highlighting of the codeblocks I added the ",[179,34504,34505],{},"material-palenight"," theme by adding the theme to the ",[179,34508,34509],{},"content"," object in the ",[179,34512,33252],{},[299,34514,34516],{"className":8734,"code":34515,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n//...\n content: {\n    highlight: {\n      theme: 'github-dark'\n    },\n  },\n  //...\n})\n",[179,34517,34518,34528,34533,34538,34543,34551,34555,34559,34563],{"__ignoreMap":307},[1736,34519,34520,34522,34524,34526],{"class":1738,"line":1739},[1736,34521,6632],{"class":4866},[1736,34523,30438],{"class":4866},[1736,34525,33282],{"class":2674},[1736,34527,5122],{"class":1912},[1736,34529,34530],{"class":1738,"line":748},[1736,34531,34532],{"class":6820},"//...\n",[1736,34534,34535],{"class":1738,"line":756},[1736,34536,34537],{"class":1912}," content: {\n",[1736,34539,34540],{"class":1738,"line":1755},[1736,34541,34542],{"class":1912},"    highlight: {\n",[1736,34544,34545,34548],{"class":1738,"line":1761},[1736,34546,34547],{"class":1912},"      theme: ",[1736,34549,34550],{"class":1935},"'github-dark'\n",[1736,34552,34553],{"class":1738,"line":1767},[1736,34554,8553],{"class":1912},[1736,34556,34557],{"class":1738,"line":1772},[1736,34558,4929],{"class":1912},[1736,34560,34561],{"class":1738,"line":1778},[1736,34562,27594],{"class":6820},[1736,34564,34565],{"class":1738,"line":1784},[1736,34566,10582],{"class":1912},[11,34568,34569],{},"The blog page now looked pretty good although I did make some extra changes to it and improved the previous and next component as well as adding the table of contents component and a better heading for the page complete with an image and tags.",[23,34571,34573],{"id":34572},"youtube-lite-plugin","Youtube lite plugin",[11,34575,34576,34577,34581],{},"My videos were all using the ",[15,34578,34580],{"href":34579},"/blog/nuxt-lite-youtube-embeds","youtube lite plugin"," which enhances performance when loading lots of youtube videos. In order to get this working I first needed to install the npm package.",[299,34583,34585],{"className":2665,"code":34584,"language":2667,"meta":307,"style":307},"npm i -D lite-youtube-embed\n",[179,34586,34587],{"__ignoreMap":307},[1736,34588,34589,34591,34593,34595],{"class":1738,"line":1739},[1736,34590,6565],{"class":2674},[1736,34592,6568],{"class":1935},[1736,34594,21717],{"class":1918},[1736,34596,34597],{"class":1935}," lite-youtube-embed\n",[11,34599,34600],{},"I then created a plugins folder and added the youtube lite plugin to it.",[299,34602,34604],{"className":8734,"code":34603,"language":8736,"meta":307,"style":307},"import 'lite-youtube-embed'\nexport default defineNuxtPlugin(() => {})\n",[179,34605,34606,34613],{"__ignoreMap":307},[1736,34607,34608,34610],{"class":1738,"line":1739},[1736,34609,4996],{"class":4866},[1736,34611,34612],{"class":1935}," 'lite-youtube-embed'\n",[1736,34614,34615,34617,34619,34622,34624,34626],{"class":1738,"line":748},[1736,34616,6632],{"class":4866},[1736,34618,30438],{"class":4866},[1736,34620,34621],{"class":2674}," defineNuxtPlugin",[1736,34623,10636],{"class":1912},[1736,34625,7013],{"class":4866},[1736,34627,12817],{"class":1912},[11,34629,34630,34631,34633],{},"In Nuxt 3 plugins are automatically imported which is very cool indeed, so I didn't have to add it to the ",[179,34632,33252],{}," file. However I did need to add the CSS for the plugin to the css array in the config.",[299,34635,34637],{"className":8734,"code":34636,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n  //...\n  css: [\n    '~/assets/css/main.css',\n    '~/node_modules/lite-youtube-embed/src/lite-yt-embed.css',\n  ],\n  //...\n})\n",[179,34638,34639,34649,34653,34657,34663,34670,34674,34678],{"__ignoreMap":307},[1736,34640,34641,34643,34645,34647],{"class":1738,"line":1739},[1736,34642,6632],{"class":4866},[1736,34644,30438],{"class":4866},[1736,34646,33282],{"class":2674},[1736,34648,5122],{"class":1912},[1736,34650,34651],{"class":1738,"line":748},[1736,34652,27594],{"class":6820},[1736,34654,34655],{"class":1738,"line":756},[1736,34656,34001],{"class":1912},[1736,34658,34659,34661],{"class":1738,"line":1755},[1736,34660,34006],{"class":1935},[1736,34662,1939],{"class":1912},[1736,34664,34665,34668],{"class":1738,"line":1761},[1736,34666,34667],{"class":1935},"    '~/node_modules/lite-youtube-embed/src/lite-yt-embed.css'",[1736,34669,1939],{"class":1912},[1736,34671,34672],{"class":1738,"line":1767},[1736,34673,33299],{"class":1912},[1736,34675,34676],{"class":1738,"line":1772},[1736,34677,27594],{"class":6820},[1736,34679,34680],{"class":1738,"line":1778},[1736,34681,10582],{"class":1912},[11,34683,34684,34685,34687],{},"I then needed to add the following to the ",[179,34686,33252],{}," file so that the component could be transpiled correctly as this was a custom element.",[299,34689,34691],{"className":8734,"code":34690,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n  //...\n  build: {\n    transpile: ['lite-youtube'],\n  },\n  vue: {\n    compilerOptions: {\n      isCustomElement: tag => ['lite-youtube'].includes(tag),\n    },\n  },\n  //...\n})\n",[179,34692,34693,34703,34707,34712,34722,34726,34731,34736,34761,34765,34769,34773],{"__ignoreMap":307},[1736,34694,34695,34697,34699,34701],{"class":1738,"line":1739},[1736,34696,6632],{"class":4866},[1736,34698,30438],{"class":4866},[1736,34700,33282],{"class":2674},[1736,34702,5122],{"class":1912},[1736,34704,34705],{"class":1738,"line":748},[1736,34706,27594],{"class":6820},[1736,34708,34709],{"class":1738,"line":756},[1736,34710,34711],{"class":1912},"  build: {\n",[1736,34713,34714,34717,34720],{"class":1738,"line":1755},[1736,34715,34716],{"class":1912},"    transpile: [",[1736,34718,34719],{"class":1935},"'lite-youtube'",[1736,34721,16460],{"class":1912},[1736,34723,34724],{"class":1738,"line":1761},[1736,34725,4929],{"class":1912},[1736,34727,34728],{"class":1738,"line":1767},[1736,34729,34730],{"class":1912},"  vue: {\n",[1736,34732,34733],{"class":1738,"line":1772},[1736,34734,34735],{"class":1912},"    compilerOptions: {\n",[1736,34737,34738,34741,34743,34746,34748,34750,34752,34755,34758],{"class":1738,"line":1778},[1736,34739,34740],{"class":2674},"      isCustomElement",[1736,34742,3065],{"class":1912},[1736,34744,34745],{"class":5036},"tag",[1736,34747,10208],{"class":4866},[1736,34749,8409],{"class":1912},[1736,34751,34719],{"class":1935},[1736,34753,34754],{"class":1912},"].",[1736,34756,34757],{"class":2674},"includes",[1736,34759,34760],{"class":1912},"(tag),\n",[1736,34762,34763],{"class":1738,"line":1784},[1736,34764,8553],{"class":1912},[1736,34766,34767],{"class":1738,"line":1790},[1736,34768,4929],{"class":1912},[1736,34770,34771],{"class":1738,"line":1796},[1736,34772,27594],{"class":6820},[1736,34774,34775],{"class":1738,"line":2353},[1736,34776,10582],{"class":1912},[23,34778,34780],{"id":34779},"color-mode-module","Color mode module",[11,34782,34783],{},"The color module had changed a little from Nuxt 2 to Nuxt 3. I started by first installing the module.",[299,34785,34787],{"className":2665,"code":34786,"language":2667,"meta":307,"style":307},"npm i -D @nuxtjs/color-mode\n",[179,34788,34789],{"__ignoreMap":307},[1736,34790,34791,34793,34795,34797],{"class":1738,"line":1739},[1736,34792,6565],{"class":2674},[1736,34794,6568],{"class":1935},[1736,34796,21717],{"class":1918},[1736,34798,34799],{"class":1935}," @nuxtjs/color-mode\n",[11,34801,34802,34803,34805,34806,34809,34810,829,34813,608,34816,34819],{},"I then added the module to the ",[179,34804,33252],{}," file and added the configuration for the module. This consisted of adding the ",[179,34807,34808],{},"colorMode"," object with the ",[179,34811,34812],{},"classSuffix",[179,34814,34815],{},"preference",[179,34817,34818],{},"fallback"," properties.",[299,34821,34823],{"className":8734,"code":34822,"language":8736,"meta":307,"style":307},"export default defineNuxtConfig({\n//...\n  modules: [\n    '@nuxt/image-edge',\n    '@nuxtjs/tailwindcss',\n    '@nuxt/content',\n    '@nuxtjs/color-mode',\n  ],\n  colorMode: {\n    classSuffix: '',\n    preference: 'system', // default value of $colorMode.preference\n    fallback: 'dark',\n  },\n  //...\n})\n",[179,34824,34825,34835,34839,34843,34849,34855,34861,34868,34872,34877,34887,34900,34910,34914,34918],{"__ignoreMap":307},[1736,34826,34827,34829,34831,34833],{"class":1738,"line":1739},[1736,34828,6632],{"class":4866},[1736,34830,30438],{"class":4866},[1736,34832,33282],{"class":2674},[1736,34834,5122],{"class":1912},[1736,34836,34837],{"class":1738,"line":748},[1736,34838,34532],{"class":6820},[1736,34840,34841],{"class":1738,"line":756},[1736,34842,33289],{"class":1912},[1736,34844,34845,34847],{"class":1738,"line":1755},[1736,34846,34086],{"class":1935},[1736,34848,1939],{"class":1912},[1736,34850,34851,34853],{"class":1738,"line":1761},[1736,34852,33983],{"class":1935},[1736,34854,1939],{"class":1912},[1736,34856,34857,34859],{"class":1738,"line":1767},[1736,34858,33990],{"class":1935},[1736,34860,1939],{"class":1912},[1736,34862,34863,34866],{"class":1738,"line":1772},[1736,34864,34865],{"class":1935},"    '@nuxtjs/color-mode'",[1736,34867,1939],{"class":1912},[1736,34869,34870],{"class":1738,"line":1778},[1736,34871,33299],{"class":1912},[1736,34873,34874],{"class":1738,"line":1784},[1736,34875,34876],{"class":1912},"  colorMode: {\n",[1736,34878,34879,34882,34885],{"class":1738,"line":1790},[1736,34880,34881],{"class":1912},"    classSuffix: ",[1736,34883,34884],{"class":1935},"''",[1736,34886,1939],{"class":1912},[1736,34888,34889,34892,34895,34897],{"class":1738,"line":1796},[1736,34890,34891],{"class":1912},"    preference: ",[1736,34893,34894],{"class":1935},"'system'",[1736,34896,829],{"class":1912},[1736,34898,34899],{"class":6820},"// default value of $colorMode.preference\n",[1736,34901,34902,34905,34908],{"class":1738,"line":2353},[1736,34903,34904],{"class":1912},"    fallback: ",[1736,34906,34907],{"class":1935},"'dark'",[1736,34909,1939],{"class":1912},[1736,34911,34912],{"class":1738,"line":2358},[1736,34913,4929],{"class":1912},[1736,34915,34916],{"class":1738,"line":2364},[1736,34917,27594],{"class":6820},[1736,34919,34920],{"class":1738,"line":2370},[1736,34921,10582],{"class":1912},[11,34923,34924,34925,34928],{},"For the component itself there was some slight refactoring to do adding the ",[179,34926,34927],{},"useColorMode()"," composable instead of the method. I also decided to use TypeScript and therefore added the type of Theme to be either light or dark. Previously I also had a sepia theme but decided not to keep maintaining it.",[299,34930,34932],{"className":8734,"code":34931,"language":8736,"meta":307,"style":307},"\u003Cscript setup lang=\"ts\">\ntype Theme = 'light' | 'dark'\nconst setColorTheme = (newTheme: Theme) => {\n  useColorMode().preference = newTheme\n}\n\u003C/script>\n",[179,34933,34934,34953,34958,34963,34976,34980],{"__ignoreMap":307},[1736,34935,34936,34938,34940,34943,34946,34948,34951],{"class":1738,"line":1739},[1736,34937,6657],{"class":1912},[1736,34939,21869],{"class":6696},[1736,34941,34942],{"class":2674}," setup",[1736,34944,34945],{"class":2674}," lang",[1736,34947,5062],{"class":4866},[1736,34949,34950],{"class":1935},"\"ts\"",[1736,34952,6663],{"class":1912},[1736,34954,34955],{"class":1738,"line":748},[1736,34956,34957],{"class":1912},"type Theme = 'light' | 'dark'\n",[1736,34959,34960],{"class":1738,"line":756},[1736,34961,34962],{"class":1912},"const setColorTheme = (newTheme: Theme) => {\n",[1736,34964,34965,34968,34971,34973],{"class":1738,"line":1755},[1736,34966,34967],{"class":2674},"  useColorMode",[1736,34969,34970],{"class":1912},"().preference ",[1736,34972,5062],{"class":4866},[1736,34974,34975],{"class":1912}," newTheme\n",[1736,34977,34978],{"class":1738,"line":1761},[1736,34979,1976],{"class":1912},[1736,34981,34982,34984,34986],{"class":1738,"line":1767},[1736,34983,8105],{"class":1912},[1736,34985,21869],{"class":6696},[1736,34987,6663],{"class":1912},[23,34989,34991],{"id":34990},"options-to-composition","Options to composition",[11,34993,34994,34995,34998,34999,35001],{},"All my code worked and my site was pretty much finished and ready to deploy. However I wanted to improve things and learn more about the composition API and what was different. So I started to refactor my code starting by adding ",[179,34996,34997],{},"setup"," to the ",[179,35000,33217],{}," tag.",[299,35003,35005],{"className":8734,"code":35004,"language":8736,"meta":307,"style":307},"\u003Cscript setup>\n\n\u003C/script>\n",[179,35006,35007,35017,35021],{"__ignoreMap":307},[1736,35008,35009,35011,35013,35015],{"class":1738,"line":1739},[1736,35010,6657],{"class":1912},[1736,35012,21869],{"class":6696},[1736,35014,34942],{"class":2674},[1736,35016,6663],{"class":1912},[1736,35018,35019],{"class":1738,"line":748},[1736,35020,1747],{"emptyLinePlaceholder":790},[1736,35022,35023,35025,35027],{"class":1738,"line":756},[1736,35024,8105],{"class":1912},[1736,35026,21869],{"class":6696},[1736,35028,6663],{"class":1912},[138,35030,35031],{"id":31463},"Props",[11,35033,35034],{},"Previously for props we needed to add an export default and add props to the props object.",[299,35036,35038],{"className":8734,"code":35037,"language":8736,"meta":307,"style":307},"\u003Cscript>\n  export default {\n    props: {\n      list\n    },\n  }\n\u003C/script>\n",[179,35039,35040,35048,35053,35058,35063,35067,35071],{"__ignoreMap":307},[1736,35041,35042,35044,35046],{"class":1738,"line":1739},[1736,35043,6657],{"class":1912},[1736,35045,21869],{"class":6696},[1736,35047,6663],{"class":1912},[1736,35049,35050],{"class":1738,"line":748},[1736,35051,35052],{"class":1912},"  export default {\n",[1736,35054,35055],{"class":1738,"line":756},[1736,35056,35057],{"class":1912},"    props: {\n",[1736,35059,35060],{"class":1738,"line":1755},[1736,35061,35062],{"class":1912},"      list\n",[1736,35064,35065],{"class":1738,"line":1761},[1736,35066,8553],{"class":1912},[1736,35068,35069],{"class":1738,"line":1767},[1736,35070,1971],{"class":1912},[1736,35072,35073,35075,35077],{"class":1738,"line":1772},[1736,35074,8105],{"class":1912},[1736,35076,21869],{"class":6696},[1736,35078,6663],{"class":1912},[11,35080,35081,35082,35085],{},"In Vue 3 we can use ",[179,35083,35084],{},"defineProps"," instead.",[299,35087,35089],{"className":8734,"code":35088,"language":8736,"meta":307,"style":307},"\u003Cscript setup>\n  defineProps\u003C{\n    list\n  }>()\n\u003C/script>\n",[179,35090,35091,35101,35106,35111,35116],{"__ignoreMap":307},[1736,35092,35093,35095,35097,35099],{"class":1738,"line":1739},[1736,35094,6657],{"class":1912},[1736,35096,21869],{"class":6696},[1736,35098,34942],{"class":2674},[1736,35100,6663],{"class":1912},[1736,35102,35103],{"class":1738,"line":748},[1736,35104,35105],{"class":1912},"  defineProps\u003C{\n",[1736,35107,35108],{"class":1738,"line":756},[1736,35109,35110],{"class":1912},"    list\n",[1736,35112,35113],{"class":1738,"line":1755},[1736,35114,35115],{"class":1912},"  }>()\n",[1736,35117,35118,35120,35122],{"class":1738,"line":1761},[1736,35119,8105],{"class":1912},[1736,35121,21869],{"class":6696},[1736,35123,6663],{"class":1912},[138,35125,35127],{"id":35126},"methods","Methods",[11,35129,35130,35131,35134,35135,35138],{},"I was previously setting the data of ",[179,35132,35133],{},"isOpen"," to false and then using a method with a ",[179,35136,35137],{},"toggle()"," function to change the value of the data from true to false.",[299,35140,35142],{"className":8734,"code":35141,"language":8736,"meta":307,"style":307},"\u003Cscript>\n  export default {\n    data() {\n      return {\n        isOpen: false\n      }\n    },\n    methods: {\n      toggle() {\n        this.isOpen = !this.isOpen\n      }\n    }\n  }\n\u003C/script>\n",[179,35143,35144,35152,35156,35163,35168,35178,35182,35186,35191,35198,35217,35221,35225,35229],{"__ignoreMap":307},[1736,35145,35146,35148,35150],{"class":1738,"line":1739},[1736,35147,6657],{"class":1912},[1736,35149,21869],{"class":6696},[1736,35151,6663],{"class":1912},[1736,35153,35154],{"class":1738,"line":748},[1736,35155,35052],{"class":1912},[1736,35157,35158,35161],{"class":1738,"line":756},[1736,35159,35160],{"class":2674},"    data",[1736,35162,6680],{"class":1912},[1736,35164,35165],{"class":1738,"line":1755},[1736,35166,35167],{"class":1912},"      return {\n",[1736,35169,35170,35173,35175],{"class":1738,"line":1761},[1736,35171,35172],{"class":2674},"        isOpen",[1736,35174,3065],{"class":1912},[1736,35176,35177],{"class":1918},"false\n",[1736,35179,35180],{"class":1738,"line":1767},[1736,35181,14448],{"class":1912},[1736,35183,35184],{"class":1738,"line":1772},[1736,35185,8553],{"class":1912},[1736,35187,35188],{"class":1738,"line":1778},[1736,35189,35190],{"class":1912},"    methods: {\n",[1736,35192,35193,35196],{"class":1738,"line":1784},[1736,35194,35195],{"class":2674},"      toggle",[1736,35197,6680],{"class":1912},[1736,35199,35200,35203,35206,35208,35211,35214],{"class":1738,"line":1790},[1736,35201,35202],{"class":1918},"        this",[1736,35204,35205],{"class":1912},".isOpen ",[1736,35207,5062],{"class":4866},[1736,35209,35210],{"class":4866}," !",[1736,35212,35213],{"class":1918},"this",[1736,35215,35216],{"class":1912},".isOpen\n",[1736,35218,35219],{"class":1738,"line":1796},[1736,35220,14448],{"class":1912},[1736,35222,35223],{"class":1738,"line":2353},[1736,35224,9853],{"class":1912},[1736,35226,35227],{"class":1738,"line":2358},[1736,35228,1971],{"class":1912},[1736,35230,35231,35233,35235],{"class":1738,"line":2364},[1736,35232,8105],{"class":1912},[1736,35234,21869],{"class":6696},[1736,35236,6663],{"class":1912},[11,35238,35239,35240,35243],{},"I refactored this to use ",[179,35241,35242],{},"refs"," passing in the initial value of false and toggling this value when the toggle is called. You can see how much cleaner the code looks from the previous version.",[299,35245,35247],{"className":8734,"code":35246,"language":8736,"meta":307,"style":307},"\u003Cscript setup>\n  const isOpen = ref(false)\n\n  const toggle = () => {\n    isOpen.value = !isOpen.value\n  }\n\u003C/script>\n",[179,35248,35249,35259,35264,35268,35273,35285,35289],{"__ignoreMap":307},[1736,35250,35251,35253,35255,35257],{"class":1738,"line":1739},[1736,35252,6657],{"class":1912},[1736,35254,21869],{"class":6696},[1736,35256,34942],{"class":2674},[1736,35258,6663],{"class":1912},[1736,35260,35261],{"class":1738,"line":748},[1736,35262,35263],{"class":1912},"  const isOpen = ref(false)\n",[1736,35265,35266],{"class":1738,"line":756},[1736,35267,1747],{"emptyLinePlaceholder":790},[1736,35269,35270],{"class":1738,"line":1755},[1736,35271,35272],{"class":1912},"  const toggle = () => {\n",[1736,35274,35275,35278,35280,35282],{"class":1738,"line":1761},[1736,35276,35277],{"class":1912},"    isOpen.value ",[1736,35279,5062],{"class":4866},[1736,35281,35210],{"class":4866},[1736,35283,35284],{"class":1912},"isOpen.value\n",[1736,35286,35287],{"class":1738,"line":1767},[1736,35288,1971],{"class":1912},[1736,35290,35291,35293,35295],{"class":1738,"line":1772},[1736,35292,8105],{"class":1912},[1736,35294,21869],{"class":6696},[1736,35296,6663],{"class":1912},[23,35298,35300],{"id":35299},"composeables","Composeables",[11,35302,35303],{},"I was rendering the date on my blog posts, videos and podcasts and this method was being used in multiple places.",[299,35305,35307],{"className":8734,"code":35306,"language":8736,"meta":307,"style":307},"\u003Cscript>\n  export default {\n    methods: {\n      formatDate(date) {\n        const options = { year: 'numeric', month: 'long', day: 'numeric' }\n        return new Date(date).toLocaleDateString('en', options)\n      }\n    }\n  }\n\u003C/script>\n",[179,35308,35309,35317,35321,35325,35336,35359,35377,35381,35385,35389],{"__ignoreMap":307},[1736,35310,35311,35313,35315],{"class":1738,"line":1739},[1736,35312,6657],{"class":1912},[1736,35314,21869],{"class":6696},[1736,35316,6663],{"class":1912},[1736,35318,35319],{"class":1738,"line":748},[1736,35320,35052],{"class":1912},[1736,35322,35323],{"class":1738,"line":756},[1736,35324,35190],{"class":1912},[1736,35326,35327,35330,35332,35334],{"class":1738,"line":1755},[1736,35328,35329],{"class":2674},"      formatDate",[1736,35331,7751],{"class":1912},[1736,35333,21891],{"class":5036},[1736,35335,7246],{"class":1912},[1736,35337,35338,35341,35343,35345,35347,35349,35351,35353,35355,35357],{"class":1738,"line":1761},[1736,35339,35340],{"class":4866},"        const",[1736,35342,21900],{"class":1918},[1736,35344,4911],{"class":4866},[1736,35346,21905],{"class":1912},[1736,35348,21908],{"class":1935},[1736,35350,21911],{"class":1912},[1736,35352,21914],{"class":1935},[1736,35354,21917],{"class":1912},[1736,35356,21908],{"class":1935},[1736,35358,21922],{"class":1912},[1736,35360,35361,35363,35365,35367,35369,35371,35373,35375],{"class":1738,"line":1767},[1736,35362,14749],{"class":4866},[1736,35364,15674],{"class":4866},[1736,35366,16631],{"class":2674},[1736,35368,21933],{"class":1912},[1736,35370,21936],{"class":2674},[1736,35372,7751],{"class":1912},[1736,35374,21941],{"class":1935},[1736,35376,21944],{"class":1912},[1736,35378,35379],{"class":1738,"line":1772},[1736,35380,14448],{"class":1912},[1736,35382,35383],{"class":1738,"line":1778},[1736,35384,9853],{"class":1912},[1736,35386,35387],{"class":1738,"line":1784},[1736,35388,1971],{"class":1912},[1736,35390,35391,35393,35395],{"class":1738,"line":1790},[1736,35392,8105],{"class":1912},[1736,35394,21869],{"class":6696},[1736,35396,6663],{"class":1912},[11,35398,35399,35400,35403,35404],{},"I decided to refactor this into a composable and use it in all the places I needed it. I created a new folder called ",[179,35401,35402],{},"composables"," and added a new file called ",[179,35405,35406],{},"utils.ts",[299,35408,35410],{"className":8734,"code":35409,"language":8736,"meta":307,"style":307},"export function formatDate(string) {\n  const options = { year: 'numeric', month: 'long', day: 'numeric' }\n  const date = new Date(string).toLocaleDateString('en-US', options)\n  return date\n}\n",[179,35411,35412,35427,35449,35473,35480],{"__ignoreMap":307},[1736,35413,35414,35416,35418,35421,35423,35425],{"class":1738,"line":1739},[1736,35415,6632],{"class":4866},[1736,35417,6674],{"class":4866},[1736,35419,35420],{"class":2674}," formatDate",[1736,35422,7751],{"class":1912},[1736,35424,17712],{"class":5036},[1736,35426,7246],{"class":1912},[1736,35428,35429,35431,35433,35435,35437,35439,35441,35443,35445,35447],{"class":1738,"line":748},[1736,35430,7824],{"class":4866},[1736,35432,21900],{"class":1918},[1736,35434,4911],{"class":4866},[1736,35436,21905],{"class":1912},[1736,35438,21908],{"class":1935},[1736,35440,21911],{"class":1912},[1736,35442,21914],{"class":1935},[1736,35444,21917],{"class":1912},[1736,35446,21908],{"class":1935},[1736,35448,21922],{"class":1912},[1736,35450,35451,35453,35456,35458,35460,35462,35465,35467,35469,35471],{"class":1738,"line":756},[1736,35452,7824],{"class":4866},[1736,35454,35455],{"class":1918}," date",[1736,35457,4911],{"class":4866},[1736,35459,15674],{"class":4866},[1736,35461,16631],{"class":2674},[1736,35463,35464],{"class":1912},"(string).",[1736,35466,21936],{"class":2674},[1736,35468,7751],{"class":1912},[1736,35470,22206],{"class":1935},[1736,35472,21944],{"class":1912},[1736,35474,35475,35477],{"class":1738,"line":1755},[1736,35476,6685],{"class":4866},[1736,35478,35479],{"class":1912}," date\n",[1736,35481,35482],{"class":1738,"line":1761},[1736,35483,1976],{"class":1912},[11,35485,35486,35491,35492,35495],{},[15,35487,35490],{"href":35488,"rel":35489},"https://nuxt.com/docs/guide/directory-structure/composables",[19],"Composeables in Nuxt 3"," are amazing. I could now use this composable in any of my components without importing it thanks to auto imports. Meaning I could just use ",[179,35493,35494],{},"formatDate(date)"," in any of my components or pages. This makes your code much more manageable and reusable.",[23,35497,35499],{"id":35498},"constants","Constants",[11,35501,35502,35503,35506,35507,35510,35511,35513,35514,35517],{},"My navigation links were being used in multiple places and I wanted to refactor this into a constant. This meant I could have the list of links in one place and import them into the components where I needed them such as the Navigation component which contained the main navigation and the Footer component which had the same links but a different style and was not using the ",[179,35504,35505],{},"\u003Cnav>"," element. I created a ",[179,35508,35509],{},"navigation.ts"," file in a folder called ",[179,35512,35498],{}," added the links. I could then import ",[179,35515,35516],{},"NavLinks"," into the components where I needed them. This is not a Nuxt 3 feature, just something I did to keep code more maintainable.",[299,35519,35521],{"className":8734,"code":35520,"language":8736,"meta":307,"style":307},"export const NavLinks = [\n  {\n    url: '/about',\n    link: 'About',\n  },\n  {\n    url: '/videos',\n    link: 'Videos',\n  },\n  {\n    url: '/podcasts',\n    link: 'Podcasts',\n  },\n  {\n    url: '/courses',\n    link: 'Courses',\n  },\n  {\n    url: '/blog',\n    link: 'Blog',\n  },\n]\n",[179,35522,35523,35536,35540,35550,35559,35563,35567,35576,35585,35589,35593,35602,35611,35615,35619,35628,35637,35641,35645,35654,35663,35667],{"__ignoreMap":307},[1736,35524,35525,35527,35529,35532,35534],{"class":1738,"line":1739},[1736,35526,6632],{"class":4866},[1736,35528,7002],{"class":4866},[1736,35530,35531],{"class":1918}," NavLinks",[1736,35533,4911],{"class":4866},[1736,35535,28906],{"class":1912},[1736,35537,35538],{"class":1738,"line":748},[1736,35539,17704],{"class":1912},[1736,35541,35542,35545,35548],{"class":1738,"line":756},[1736,35543,35544],{"class":1912},"    url: ",[1736,35546,35547],{"class":1935},"'/about'",[1736,35549,1939],{"class":1912},[1736,35551,35552,35555,35557],{"class":1738,"line":1755},[1736,35553,35554],{"class":1912},"    link: ",[1736,35556,27047],{"class":1935},[1736,35558,1939],{"class":1912},[1736,35560,35561],{"class":1738,"line":1761},[1736,35562,4929],{"class":1912},[1736,35564,35565],{"class":1738,"line":1767},[1736,35566,17704],{"class":1912},[1736,35568,35569,35571,35574],{"class":1738,"line":1772},[1736,35570,35544],{"class":1912},[1736,35572,35573],{"class":1935},"'/videos'",[1736,35575,1939],{"class":1912},[1736,35577,35578,35580,35583],{"class":1738,"line":1778},[1736,35579,35554],{"class":1912},[1736,35581,35582],{"class":1935},"'Videos'",[1736,35584,1939],{"class":1912},[1736,35586,35587],{"class":1738,"line":1784},[1736,35588,4929],{"class":1912},[1736,35590,35591],{"class":1738,"line":1790},[1736,35592,17704],{"class":1912},[1736,35594,35595,35597,35600],{"class":1738,"line":1796},[1736,35596,35544],{"class":1912},[1736,35598,35599],{"class":1935},"'/podcasts'",[1736,35601,1939],{"class":1912},[1736,35603,35604,35606,35609],{"class":1738,"line":2353},[1736,35605,35554],{"class":1912},[1736,35607,35608],{"class":1935},"'Podcasts'",[1736,35610,1939],{"class":1912},[1736,35612,35613],{"class":1738,"line":2358},[1736,35614,4929],{"class":1912},[1736,35616,35617],{"class":1738,"line":2364},[1736,35618,17704],{"class":1912},[1736,35620,35621,35623,35626],{"class":1738,"line":2370},[1736,35622,35544],{"class":1912},[1736,35624,35625],{"class":1935},"'/courses'",[1736,35627,1939],{"class":1912},[1736,35629,35630,35632,35635],{"class":1738,"line":2376},[1736,35631,35554],{"class":1912},[1736,35633,35634],{"class":1935},"'Courses'",[1736,35636,1939],{"class":1912},[1736,35638,35639],{"class":1738,"line":2381},[1736,35640,4929],{"class":1912},[1736,35642,35643],{"class":1738,"line":2387},[1736,35644,17704],{"class":1912},[1736,35646,35647,35649,35652],{"class":1738,"line":2393},[1736,35648,35544],{"class":1912},[1736,35650,35651],{"class":1935},"'/blog'",[1736,35653,1939],{"class":1912},[1736,35655,35656,35658,35661],{"class":1738,"line":2398},[1736,35657,35554],{"class":1912},[1736,35659,35660],{"class":1935},"'Blog'",[1736,35662,1939],{"class":1912},[1736,35664,35665],{"class":1738,"line":2404},[1736,35666,4929],{"class":1912},[1736,35668,35669],{"class":1738,"line":6959},[1736,35670,8420],{"class":1912},[23,35672,35673],{"id":15559},"TypeScript",[11,35675,35676],{},"I really wanted to make use of the fact that Nuxt 3 is TypeScript first and I wanted to learn more about TypeScript as I am certainly no expert. It is not exactly easy to get started as the Nuxt docs do not give a lot of TypeScrip examples but I am sure with time these will be improved.",[11,35678,35679,35680,34998,35683,35685],{},"I started by adding the ",[179,35681,35682],{},"lang=\"ts\"",[179,35684,33217],{}," tag of one of the components. This meant I could now use TypeScript in this component. The great thing about this is that I could refactor one by one taking my time to make sure I knew what I was doing for each component.",[299,35687,35689],{"className":25266,"code":35688,"language":25268,"meta":307,"style":307},"\u003Cscript setup lang=\"ts\">\n\n\u003C/script>\n",[179,35690,35691,35704,35708],{"__ignoreMap":307},[1736,35692,35693,35695,35698,35700,35702],{"class":1738,"line":1739},[1736,35694,6657],{"class":4866},[1736,35696,35697],{"class":1912},"script setup lang",[1736,35699,5062],{"class":4866},[1736,35701,34950],{"class":1935},[1736,35703,6663],{"class":4866},[1736,35705,35706],{"class":1738,"line":748},[1736,35707,1747],{"emptyLinePlaceholder":790},[1736,35709,35710,35712,35714],{"class":1738,"line":756},[1736,35711,8105],{"class":4866},[1736,35713,21869],{"class":1912},[1736,35715,6663],{"class":4866},[138,35717,35719],{"id":35718},"types-file","Types file",[11,35721,35722,35723,35726],{},"Instead of adding types to my props direct in the component I created a ",[179,35724,35725],{},"types.ts"," file and added my types here.",[299,35728,35730],{"className":8734,"code":35729,"language":8736,"meta":307,"style":307},"export type Sections = 'blog' | 'podcasts' | 'videos' | 'courses'\n",[179,35731,35732],{"__ignoreMap":307},[1736,35733,35734,35736,35738,35741,35743,35746,35749,35752,35754,35757,35759],{"class":1738,"line":1739},[1736,35735,6632],{"class":4866},[1736,35737,6635],{"class":4866},[1736,35739,35740],{"class":2674}," Sections",[1736,35742,4911],{"class":4866},[1736,35744,35745],{"class":1935}," 'blog'",[1736,35747,35748],{"class":4866}," |",[1736,35750,35751],{"class":1935}," 'podcasts'",[1736,35753,35748],{"class":4866},[1736,35755,35756],{"class":1935}," 'videos'",[1736,35758,35748],{"class":4866},[1736,35760,35761],{"class":1935}," 'courses'\n",[11,35763,35764],{},"This meant I could then import the types and use them in various components.",[299,35766,35768],{"className":25266,"code":35767,"language":25268,"meta":307,"style":307},"\u003Cscript setup lang=\"ts\">\nimport type { Sections } from '~/types'\ndefineProps\u003C{\n  section: Sections\n}>()\n\u003C/script>\n",[179,35769,35770,35782,35796,35803,35813,35818],{"__ignoreMap":307},[1736,35771,35772,35774,35776,35778,35780],{"class":1738,"line":1739},[1736,35773,6657],{"class":4866},[1736,35775,35697],{"class":1912},[1736,35777,5062],{"class":4866},[1736,35779,34950],{"class":1935},[1736,35781,6663],{"class":4866},[1736,35783,35784,35786,35788,35791,35793],{"class":1738,"line":748},[1736,35785,4996],{"class":4866},[1736,35787,6635],{"class":4866},[1736,35789,35790],{"class":1912}," { Sections } ",[1736,35792,5002],{"class":4866},[1736,35794,35795],{"class":1935}," '~/types'\n",[1736,35797,35798,35800],{"class":1738,"line":756},[1736,35799,35084],{"class":2674},[1736,35801,35802],{"class":1912},"\u003C{\n",[1736,35804,35805,35808,35810],{"class":1738,"line":1755},[1736,35806,35807],{"class":5036},"  section",[1736,35809,1087],{"class":4866},[1736,35811,35812],{"class":2674}," Sections\n",[1736,35814,35815],{"class":1738,"line":1761},[1736,35816,35817],{"class":1912},"}>()\n",[1736,35819,35820,35822,35824],{"class":1738,"line":1767},[1736,35821,8105],{"class":4866},[1736,35823,21869],{"class":1912},[1736,35825,6663],{"class":4866},[138,35827,35829],{"id":35828},"types-for-nuxt-content","Types for nuxt content",[11,35831,35832,35833,12695,35836,35839],{},"The props for my content coming form Nuxt content was a little more complex. I had to import ",[179,35834,35835],{},"ParsedContent",[179,35837,35838],{},"@nuxt/content"," and then add the type to the props I was using for my blog post.",[299,35841,35843],{"className":25266,"code":35842,"language":25268,"meta":307,"style":307},"import type { ParsedContent } from '@nuxt/content/dist/runtime/types'\nexport type Sections = 'blog' | 'podcasts' | 'videos' | 'courses'\n\nexport interface BlogPost extends ParsedContent {\n  title: string\n  date: string\n  description: string\n  url?: string\n  image: string\n  alt: string\n  ogImage?: string\n  provider: string\n  tags: string[]\n  published?: boolean\n}\n",[179,35844,35845,35859,35883,35887,35903,35912,35921,35929,35938,35947,35955,35964,35973,35985,35995],{"__ignoreMap":307},[1736,35846,35847,35849,35851,35854,35856],{"class":1738,"line":1739},[1736,35848,4996],{"class":4866},[1736,35850,6635],{"class":4866},[1736,35852,35853],{"class":1912}," { ParsedContent } ",[1736,35855,5002],{"class":4866},[1736,35857,35858],{"class":1935}," '@nuxt/content/dist/runtime/types'\n",[1736,35860,35861,35863,35865,35867,35869,35871,35873,35875,35877,35879,35881],{"class":1738,"line":748},[1736,35862,6632],{"class":4866},[1736,35864,6635],{"class":4866},[1736,35866,35740],{"class":2674},[1736,35868,4911],{"class":4866},[1736,35870,35745],{"class":1935},[1736,35872,35748],{"class":4866},[1736,35874,35751],{"class":1935},[1736,35876,35748],{"class":4866},[1736,35878,35756],{"class":1935},[1736,35880,35748],{"class":4866},[1736,35882,35761],{"class":1935},[1736,35884,35885],{"class":1738,"line":756},[1736,35886,1747],{"emptyLinePlaceholder":790},[1736,35888,35889,35891,35893,35896,35898,35901],{"class":1738,"line":1755},[1736,35890,6632],{"class":4866},[1736,35892,13673],{"class":4866},[1736,35894,35895],{"class":2674}," BlogPost",[1736,35897,13716],{"class":4866},[1736,35899,35900],{"class":2674}," ParsedContent",[1736,35902,4914],{"class":1912},[1736,35904,35905,35908,35910],{"class":1738,"line":1761},[1736,35906,35907],{"class":5036},"  title",[1736,35909,1087],{"class":4866},[1736,35911,6866],{"class":1918},[1736,35913,35914,35917,35919],{"class":1738,"line":1767},[1736,35915,35916],{"class":5036},"  date",[1736,35918,1087],{"class":4866},[1736,35920,6866],{"class":1918},[1736,35922,35923,35925,35927],{"class":1738,"line":1772},[1736,35924,16324],{"class":5036},[1736,35926,1087],{"class":4866},[1736,35928,6866],{"class":1918},[1736,35930,35931,35934,35936],{"class":1738,"line":1778},[1736,35932,35933],{"class":5036},"  url",[1736,35935,8830],{"class":4866},[1736,35937,6866],{"class":1918},[1736,35939,35940,35943,35945],{"class":1738,"line":1784},[1736,35941,35942],{"class":5036},"  image",[1736,35944,1087],{"class":4866},[1736,35946,6866],{"class":1918},[1736,35948,35949,35951,35953],{"class":1738,"line":1790},[1736,35950,6861],{"class":5036},[1736,35952,1087],{"class":4866},[1736,35954,6866],{"class":1918},[1736,35956,35957,35960,35962],{"class":1738,"line":1796},[1736,35958,35959],{"class":5036},"  ogImage",[1736,35961,8830],{"class":4866},[1736,35963,6866],{"class":1918},[1736,35965,35966,35969,35971],{"class":1738,"line":2353},[1736,35967,35968],{"class":5036},"  provider",[1736,35970,1087],{"class":4866},[1736,35972,6866],{"class":1918},[1736,35974,35975,35978,35980,35982],{"class":1738,"line":2358},[1736,35976,35977],{"class":5036},"  tags",[1736,35979,1087],{"class":4866},[1736,35981,6841],{"class":1918},[1736,35983,35984],{"class":1912},"[]\n",[1736,35986,35987,35990,35992],{"class":1738,"line":2364},[1736,35988,35989],{"class":5036},"  published",[1736,35991,8830],{"class":4866},[1736,35993,35994],{"class":1918}," boolean\n",[1736,35996,35997],{"class":1738,"line":2370},[1736,35998,1976],{"class":1912},[11,36000,36001],{},"I could then import this type and use it in pages or components.",[299,36003,36005],{"className":25266,"code":36004,"language":25268,"meta":307,"style":307},"\u003Cscript setup lang=\"ts\">\nimport type { BlogPost } from '~/types'\n\nconst { data: articles } = await useAsyncData(`articles-${slug}`\n  () => queryContent\u003CBlogPost>('blog')\n    .where({ published: { $ne: false } })\n    .sort({ date: -1 })\n    .find(),\n)\n\u003C/script>\n",[179,36006,36007,36019,36032,36036,36064,36082,36094,36108,36116,36120],{"__ignoreMap":307},[1736,36008,36009,36011,36013,36015,36017],{"class":1738,"line":1739},[1736,36010,6657],{"class":4866},[1736,36012,35697],{"class":1912},[1736,36014,5062],{"class":4866},[1736,36016,34950],{"class":1935},[1736,36018,6663],{"class":4866},[1736,36020,36021,36023,36025,36028,36030],{"class":1738,"line":748},[1736,36022,4996],{"class":4866},[1736,36024,6635],{"class":4866},[1736,36026,36027],{"class":1912}," { BlogPost } ",[1736,36029,5002],{"class":4866},[1736,36031,35795],{"class":1935},[1736,36033,36034],{"class":1738,"line":756},[1736,36035,1747],{"emptyLinePlaceholder":790},[1736,36037,36038,36040,36042,36044,36046,36048,36050,36052,36054,36056,36058,36060,36062],{"class":1738,"line":1755},[1736,36039,5029],{"class":4866},[1736,36041,7827],{"class":1912},[1736,36043,33342],{"class":5036},[1736,36045,3065],{"class":1912},[1736,36047,33347],{"class":1918},[1736,36049,7832],{"class":1912},[1736,36051,5062],{"class":4866},[1736,36053,18389],{"class":4866},[1736,36055,33356],{"class":2674},[1736,36057,7751],{"class":1912},[1736,36059,34274],{"class":1935},[1736,36061,34232],{"class":1912},[1736,36063,18693],{"class":1935},[1736,36065,36066,36068,36070,36072,36074,36076,36078,36080],{"class":1738,"line":1761},[1736,36067,33368],{"class":1912},[1736,36069,7013],{"class":4866},[1736,36071,33373],{"class":2674},[1736,36073,6657],{"class":1912},[1736,36075,34293],{"class":2674},[1736,36077,34296],{"class":1912},[1736,36079,33378],{"class":1935},[1736,36081,7045],{"class":1912},[1736,36083,36084,36086,36088,36090,36092],{"class":1738,"line":1767},[1736,36085,33385],{"class":1912},[1736,36087,33503],{"class":2674},[1736,36089,33506],{"class":1912},[1736,36091,33509],{"class":1918},[1736,36093,33512],{"class":1912},[1736,36095,36096,36098,36100,36102,36104,36106],{"class":1738,"line":1772},[1736,36097,33385],{"class":1912},[1736,36099,33546],{"class":2674},[1736,36101,33549],{"class":1912},[1736,36103,9419],{"class":4866},[1736,36105,10249],{"class":1918},[1736,36107,10691],{"class":1912},[1736,36109,36110,36112,36114],{"class":1738,"line":1778},[1736,36111,33385],{"class":1912},[1736,36113,7770],{"class":2674},[1736,36115,34336],{"class":1912},[1736,36117,36118],{"class":1738,"line":1784},[1736,36119,7045],{"class":1912},[1736,36121,36122,36124,36126],{"class":1738,"line":1790},[1736,36123,8105],{"class":4866},[1736,36125,21869],{"class":1912},[1736,36127,6663],{"class":4866},[138,36129,36131],{"id":36130},"omiting-types","Omiting types",[11,36133,36134,36135,36138,36139,36142,36143,36145],{},"For the preview components where I omit the body tag I created a type called ",[179,36136,36137],{},"BlogPostPreview"," and used the ",[179,36140,36141],{},"Omit"," type to omit the body tag from the ",[179,36144,34293],{}," type.",[299,36147,36149],{"className":25266,"code":36148,"language":25268,"meta":307,"style":307},"export type BlogPostPreview = Omit\u003CBlogPost, 'body'>\n",[179,36150,36151],{"__ignoreMap":307},[1736,36152,36153,36155,36157,36160,36162,36165,36167,36169,36171,36173],{"class":1738,"line":1739},[1736,36154,6632],{"class":4866},[1736,36156,6635],{"class":4866},[1736,36158,36159],{"class":2674}," BlogPostPreview",[1736,36161,4911],{"class":4866},[1736,36163,36164],{"class":2674}," Omit",[1736,36166,6657],{"class":1912},[1736,36168,34293],{"class":2674},[1736,36170,829],{"class":1912},[1736,36172,33524],{"class":1935},[1736,36174,6663],{"class":1912},[11,36176,36177],{},"Then in my component I could import this type and use it in the props.",[299,36179,36181],{"className":25266,"code":36180,"language":25268,"meta":307,"style":307},"\u003Cscript setup lang=\"ts\">\nimport type { BlogPostPreview } from '~/types'\n\ndefineProps\u003C{\n  item: BlogPostPreview\n}>()\n\u003C/script>\n",[179,36182,36183,36195,36208,36212,36218,36227,36231],{"__ignoreMap":307},[1736,36184,36185,36187,36189,36191,36193],{"class":1738,"line":1739},[1736,36186,6657],{"class":4866},[1736,36188,35697],{"class":1912},[1736,36190,5062],{"class":4866},[1736,36192,34950],{"class":1935},[1736,36194,6663],{"class":4866},[1736,36196,36197,36199,36201,36204,36206],{"class":1738,"line":748},[1736,36198,4996],{"class":4866},[1736,36200,6635],{"class":4866},[1736,36202,36203],{"class":1912}," { BlogPostPreview } ",[1736,36205,5002],{"class":4866},[1736,36207,35795],{"class":1935},[1736,36209,36210],{"class":1738,"line":756},[1736,36211,1747],{"emptyLinePlaceholder":790},[1736,36213,36214,36216],{"class":1738,"line":1755},[1736,36215,35084],{"class":2674},[1736,36217,35802],{"class":1912},[1736,36219,36220,36222,36224],{"class":1738,"line":1761},[1736,36221,13739],{"class":5036},[1736,36223,1087],{"class":4866},[1736,36225,36226],{"class":2674}," BlogPostPreview\n",[1736,36228,36229],{"class":1738,"line":1767},[1736,36230,35817],{"class":1912},[1736,36232,36233,36235,36237],{"class":1738,"line":1772},[1736,36234,8105],{"class":4866},[1736,36236,21869],{"class":1912},[1736,36238,6663],{"class":4866},[23,36240,3294],{"id":3293},[11,36242,36243,36244,36246],{},"I am still unsure if what I am doing here is entirely correct. All TypeScript errors have gone which is great but if this is the correct way of doing things or not I really don't know and there are not much docs to go by. Sometimes hovering over the the props for my articles gives me the type of ",[179,36245,13929],{}," even though I have defined types so I am not sure if this is an issue with the content module or my lack of knowledge of TypeScript. If anyone else has added types for Nuxt content I would love to hear your feedback.",[11,36248,36249],{},"Either way. Even though it is not perfect I am pretty happy with having moved everything to TypeScript. I don't feel it is that difficult and it ensures I am using the correct types for my props and data and helps me find errors quicker.",[11,36251,36252],{},"Overall I am really happy with the progress I have made with Nuxt 3. I am still learning and I am sure there are many things I can improve on and there are still lots more features I can explore. Send me a pr or DM me if you see anything that needs improving. All feedback is welcome.",[1713,36254,36255],{},[11,36256,36257],{},"I am really excited to finally see the release of Nuxt 3. I know how much work has gone into it and I really think the team have done an amazing job.",[138,36259,36261],{"id":36260},"community-thanks","Community thanks",[11,36263,36264,36265,36267,36268,36273,36274,36277,36278,36281,36282,36287],{},"Special thanks to members of the community who pointed out that my site wasn't working as it should when I wasn't using ",[179,36266,33451],{}," for querying my content and ",[15,36269,36272],{"href":36270,"rel":36271},"https://twitter.com/JoshDeltener",[19],"Josh Deltner"," for pointing out that my site had no favicon because I completely forget to copy over the ",[179,36275,36276],{},"static"," folder. Note this is now called the ",[179,36279,36280],{},"public"," folder in Nuxt 3. And of course ",[15,36283,36286],{"href":36284,"rel":36285},"https://twitter.com/danielcroe",[19],"Daniel Roe"," for answering my constant queries on migration, TypeScript and more.",[11,36289,36290,36291,36296],{},"Also the ",[15,36292,36295],{"href":36293,"rel":36294},"https://github.com/nuxt/movies",[19],"movies repo"," was a great reference for me to see how the Nuxt team have implemented Nuxt 3. I highly recommend checking it out.",[138,36298,36300],{"id":36299},"other-things-not-covered","Other things not covered",[11,36302,36303,36304,36307,36308,36310,36311,36313],{},"Other things I didn't cover in this post were ",[179,36305,36306],{},"useHead"," for meta data which has changed and you can see an example in the ",[179,36309,33097],{}," file although I may revert to adding this back to the ",[179,36312,34061],{}," file instead.",[11,36315,36316,36317,36320],{},"Also the 404 page is now created by adding a ",[179,36318,36319],{},"[...slug].vue"," file in the pages folder. This will then catch all routes that are not found and render what is inside this file instead.",[11,36322,36323,36324,36328],{},"Also I added testing with ",[15,36325,36327],{"href":22726,"rel":36326},[19],"Playwright"," and generated end to end tests using Codegen, Playwrights test generator, to test my site and ensure everything works. I will cover this in a future post.",[23,36330,17156],{"id":17155},[70,36332,36333,36340,36347,36353],{},[73,36334,36335],{},[15,36336,36339],{"href":36337,"rel":36338},"https://v3.nuxtjs.org/",[19],"Nuxt 3 Docs",[73,36341,36342],{},[15,36343,36346],{"href":36344,"rel":36345},"https://content.nuxtjs.org/",[19],"Nuxt Content",[73,36348,36349],{},[15,36350,36352],{"href":33027,"rel":36351},[19],"My repo",[73,36354,36355],{},[15,36356,36358],{"href":36293,"rel":36357},[19],"Movies repo",[2011,36360,36361],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":36363},[36364,36365,36366,36367,36368,36369,36370,36371,36372,36373,36374,36375,36376,36377,36378,36382,36383,36384,36389,36393],{"id":33035,"depth":748,"text":33036},{"id":33088,"depth":748,"text":33089},{"id":33154,"depth":748,"text":33155},{"id":33210,"depth":748,"text":33211},{"id":33221,"depth":748,"text":33222},{"id":33887,"depth":748,"text":33888},{"id":33911,"depth":748,"text":33912},{"id":34022,"depth":748,"text":34023},{"id":34171,"depth":748,"text":34172},{"id":34181,"depth":748,"text":34182},{"id":34343,"depth":748,"text":34344},{"id":34421,"depth":748,"text":34422},{"id":34572,"depth":748,"text":34573},{"id":34779,"depth":748,"text":34780},{"id":34990,"depth":748,"text":34991,"children":36379},[36380,36381],{"id":31463,"depth":756,"text":35031},{"id":35126,"depth":756,"text":35127},{"id":35299,"depth":748,"text":35300},{"id":35498,"depth":748,"text":35499},{"id":15559,"depth":748,"text":35673,"children":36385},[36386,36387,36388],{"id":35718,"depth":756,"text":35719},{"id":35828,"depth":756,"text":35829},{"id":36130,"depth":756,"text":36131},{"id":3293,"depth":748,"text":3294,"children":36390},[36391,36392],{"id":36260,"depth":756,"text":36261},{"id":36299,"depth":756,"text":36300},{"id":17155,"depth":748,"text":17156},"2022-11-27","My personal website was built many years ago and had collected quite a large amount of code as I used my site to play around and experiment new features of Nuxt. It took me ages to finally decide to migrate cause lets face it, we all hate migrations. But I finally did it and I'm so glad I did. I'm going to share with you the steps I took to migrate my site from Nuxt 2 to Nuxt 3.","v1640965793/debbie.codes/blog/2022/nuxt3_q10xtr.png",{"alt":36398,"ogImage":36399},"Nuxt logo with text of Migration to Nuxt 3","https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1640965793/debbie.codes/blog/2022/nuxt3_q10xtr.png","/blog/migrating-nuxt2-nuxt3",{"title":33019,"description":36395},"blog/migrating-nuxt2-nuxt3",[5239],"ZhTTiZJ5xP2lyG7lI4Q1VpWEcd3USepwRhThXloVzt0",{"id":36406,"title":36407,"body":36408,"canonical":788,"date":20912,"description":36701,"extension":786,"featured":787,"image":36702,"meta":36703,"navigation":790,"ogimage":788,"path":36705,"provider":3460,"published":790,"seo":36706,"stem":36707,"tags":36708,"url":788,"__hash__":36710},"blog/blog/minimizing-svgs.md","Minimizing SVGs",{"type":8,"value":36409,"toc":36699},[36410,36419,36426,36474,36477,36499,36502,36653,36662,36668,36671,36677,36680,36689,36696],[11,36411,36412,36413,36418],{},"Recently I decided to add the dev.to icon to my site in the footer. From the ",[15,36414,36417],{"href":36415,"rel":36416},"https://dev.to/brand",[19],"dev.to site's brand page"," I can easily get the icon in svg format. Once I download this icon I can drag and drop it to the icons folder in my Nuxt site.",[11,36420,36421,36422,36425],{},"I am using the svgs inline so I need to import it in the script tag with ",[179,36423,36424],{},"?inline"," at the end. Then in the components object I add the component name.",[299,36427,36429],{"className":4894,"code":36428,"language":4896,"meta":307,"style":307},"\u003Cscript>\n  import IconDevTo from '@/assets/icons/dev-rainbow.svg?inline'\n  export default {\n    components: {\n      IconDevTo\n    }\n  }\n\u003C/script>\n",[179,36430,36431,36439,36444,36448,36453,36458,36462,36466],{"__ignoreMap":307},[1736,36432,36433,36435,36437],{"class":1738,"line":1739},[1736,36434,6657],{"class":1912},[1736,36436,21869],{"class":6696},[1736,36438,6663],{"class":1912},[1736,36440,36441],{"class":1738,"line":748},[1736,36442,36443],{"class":1912},"  import IconDevTo from '@/assets/icons/dev-rainbow.svg?inline'\n",[1736,36445,36446],{"class":1738,"line":756},[1736,36447,35052],{"class":1912},[1736,36449,36450],{"class":1738,"line":1755},[1736,36451,36452],{"class":1912},"    components: {\n",[1736,36454,36455],{"class":1738,"line":1761},[1736,36456,36457],{"class":1912},"      IconDevTo\n",[1736,36459,36460],{"class":1738,"line":1767},[1736,36461,9853],{"class":1912},[1736,36463,36464],{"class":1738,"line":1772},[1736,36465,1971],{"class":1912},[1736,36467,36468,36470,36472],{"class":1738,"line":1778},[1736,36469,8105],{"class":1912},[1736,36471,21869],{"class":6696},[1736,36473,6663],{"class":1912},[11,36475,36476],{},"This will import my svg file and so I can use it like I would any component.",[299,36478,36480],{"className":8734,"code":36479,"language":8736,"meta":307,"style":307},"\u003CIconDevTo class=\"h-10 w-10 fill-current\" />\n",[179,36481,36482],{"__ignoreMap":307},[1736,36483,36484,36486,36489,36492,36494,36497],{"class":1738,"line":1739},[1736,36485,6657],{"class":1912},[1736,36487,36488],{"class":1918},"IconDevTo",[1736,36490,36491],{"class":2674}," class",[1736,36493,5062],{"class":4866},[1736,36495,36496],{"class":1935},"\"h-10 w-10 fill-current\"",[1736,36498,6739],{"class":1912},[11,36500,36501],{},"But the problem is that this SVG is quite big and comes with a lot of code. If we open the SVG in our editor this is the code we get.",[299,36503,36507],{"className":36504,"code":36505,"language":36506,"meta":307,"style":307},"language-xml shiki shiki-themes github-light github-dark","\u003Csvg viewBox=\"0 0 235 234\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" class=\"rainbow-logo\"\n    preserveAspectRatio=\"xMinYMin meet\">\n  \u003Cg id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n    \u003Cg id=\"80K\">\n      \u003Cpolygon id=\"Shape\" fill=\"#88AEDC\" points=\"234.04 175.67 158.35 233.95 205.53 233.95 234.04 212\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" points=\"234.04 140.06 112.11 233.95 112.13 233.95 234.04 140.08\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" points=\"133.25 0.95 0.04 103.51 0.04 103.53 133.27 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F58F8E\" fill-rule=\"nonzero\" points=\"0.04 0.95 0.04 31.11 39.21 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#FEE18A\" fill-rule=\"nonzero\" points=\"39.21 0.95 0.04 31.11 0.04 67.01 85.84 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F3F095\" fill-rule=\"nonzero\" points=\"85.84 0.95 0.04 67.01 0.04 103.51 133.25 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#55C1AE\" fill-rule=\"nonzero\" points=\"133.27 0.95 0.04 103.53 0.04 139.12 179.49 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F7B3CE\" fill-rule=\"nonzero\" points=\"234.04 0.95 226.67 0.95 0.04 175.45 0.04 211.38 234.04 31.2\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#88AEDC\" fill-rule=\"nonzero\" points=\"179.49 0.95 0.04 139.12 0.04 175.45 226.67 0.95\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F58F8E\" fill-rule=\"nonzero\" points=\"234.04 31.2 0.04 211.38 0.04 233.95 18.07 233.95 234.04 67.65\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#FEE18A\" fill-rule=\"nonzero\" points=\"234.04 67.65 18.07 233.95 64.7 233.95 234.04 103.56\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F3F095\" fill-rule=\"nonzero\" points=\"234.04 103.56 64.7 233.95 112.11 233.95 234.04 140.06\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#55C1AE\" fill-rule=\"nonzero\" points=\"234.04 140.08 112.13 233.95 158.35 233.95 234.04 175.67\">\u003C/polygon>\n      \u003Cpolygon id=\"Shape\" fill=\"#F7B3CE\" fill-rule=\"nonzero\" points=\"234.04 212 205.53 233.95 234.04 233.95\">\u003C/polygon>\n      \u003Cg id=\"Group\" transform=\"translate(37.000000, 77.000000)\" fill=\"#FFFFFF\">\n        \u003Cpath d=\"M28.2371517,0.75 C32.7510836,1.7 36.0111455,3.55 39.371517,7.05 C42.4309598,10.25 44.3368421,13.9 45.1393189,18 C45.7913313,21.45 45.7913313,58.55 45.1393189,62.05 C43.4340557,71.15 35.6600619,78.25 26.0303406,79.5 C24.0241486,79.75 17.3034056,80 11.1845201,80 L-7.10542736e-15,80 L-7.10542736e-15,1.42108547e-14 L12.4383901,1.42108547e-14 C21.2656347,1.42108547e-14 25.7795666,0.2 28.2371517,0.75 Z M14.5448916,40 L14.5448916,65.6 L19.7108359,65.4 C24.174613,65.25 25.1275542,65.05 27.1337461,63.9 C31.0458204,61.6 31.0959752,61.45 31.0959752,39.7 C31.0959752,18.5 31.0959752,18.5 27.4346749,16.1 C25.6291022,14.9 24.8767802,14.75 19.9616099,14.55 L14.5448916,14.4 L14.5448916,40 Z\"\n              id=\"Combined-Shape\">\u003C/path>\n        \u003Cpath d=\"M93.7894737,7.25 L93.7894737,14.5 L68.2105263,14.5 L68.2105263,32.5 L83.7585139,32.5 L83.7585139,47 L68.2105263,47 L68.3108359,56.1 L68.4613003,65.25 L81.1504644,65.4 L93.7894737,65.5 L93.7894737,80 L78.993808,80 C62.5430341,80 59.9851393,79.7 57.3770898,77.4 C53.7157895,74.2 53.9164087,76.25 53.7659443,41.1 C53.6656347,19.2 53.8160991,8.85 54.1671827,7.45 C54.8693498,4.85 57.828483,1.65 60.4365325,0.75 C61.9913313,0.2 65.9034056,0.05 78.1411765,4.26325641e-14 L93.7894737,4.26325641e-14 L93.7894737,7.25 Z\"\n              id=\"Path\">\u003C/path>\n        \u003Cpath d=\"M125.437152,28.1 C129.148607,42.35 132.258204,53.7 132.358514,53.35 C132.508978,53 135.668731,40.95 139.430341,26.5 L146.301548,0.25 L154.125697,0.1 C160.043963,7.10542736e-15 162,0.15 162,0.6 C162,1.05 144.64644,66.8 143.643344,70.1 C142.941176,72.4 139.179567,77.1 137.073065,78.35 C134.414861,79.85 130.502786,80.1 128.095356,78.85 C125.9387,77.75 123.079876,74.45 121.625387,71.35 C120.722601,69.45 105.97709,15.35 102.566563,1.35 L102.21548,0 L110.039628,0 C117.713313,0 117.913932,0 118.31517,1.1 C118.515789,1.75 121.725697,13.9 125.437152,28.1 Z\"\n              id=\"Path\">\u003C/path>\n      \u003C/g>\n    \u003C/g>\n  \u003C/g>\n\u003C/svg>\n","xml",[179,36508,36509,36514,36519,36524,36529,36534,36539,36544,36549,36554,36559,36564,36569,36574,36579,36584,36589,36594,36599,36604,36609,36614,36619,36624,36629,36633,36638,36643,36648],{"__ignoreMap":307},[1736,36510,36511],{"class":1738,"line":1739},[1736,36512,36513],{},"\u003Csvg viewBox=\"0 0 235 234\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" class=\"rainbow-logo\"\n",[1736,36515,36516],{"class":1738,"line":748},[1736,36517,36518],{},"    preserveAspectRatio=\"xMinYMin meet\">\n",[1736,36520,36521],{"class":1738,"line":756},[1736,36522,36523],{},"  \u003Cg id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n",[1736,36525,36526],{"class":1738,"line":1755},[1736,36527,36528],{},"    \u003Cg id=\"80K\">\n",[1736,36530,36531],{"class":1738,"line":1761},[1736,36532,36533],{},"      \u003Cpolygon id=\"Shape\" fill=\"#88AEDC\" points=\"234.04 175.67 158.35 233.95 205.53 233.95 234.04 212\">\u003C/polygon>\n",[1736,36535,36536],{"class":1738,"line":1767},[1736,36537,36538],{},"      \u003Cpolygon id=\"Shape\" points=\"234.04 140.06 112.11 233.95 112.13 233.95 234.04 140.08\">\u003C/polygon>\n",[1736,36540,36541],{"class":1738,"line":1772},[1736,36542,36543],{},"      \u003Cpolygon id=\"Shape\" points=\"133.25 0.95 0.04 103.51 0.04 103.53 133.27 0.95\">\u003C/polygon>\n",[1736,36545,36546],{"class":1738,"line":1778},[1736,36547,36548],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F58F8E\" fill-rule=\"nonzero\" points=\"0.04 0.95 0.04 31.11 39.21 0.95\">\u003C/polygon>\n",[1736,36550,36551],{"class":1738,"line":1784},[1736,36552,36553],{},"      \u003Cpolygon id=\"Shape\" fill=\"#FEE18A\" fill-rule=\"nonzero\" points=\"39.21 0.95 0.04 31.11 0.04 67.01 85.84 0.95\">\u003C/polygon>\n",[1736,36555,36556],{"class":1738,"line":1790},[1736,36557,36558],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F3F095\" fill-rule=\"nonzero\" points=\"85.84 0.95 0.04 67.01 0.04 103.51 133.25 0.95\">\u003C/polygon>\n",[1736,36560,36561],{"class":1738,"line":1796},[1736,36562,36563],{},"      \u003Cpolygon id=\"Shape\" fill=\"#55C1AE\" fill-rule=\"nonzero\" points=\"133.27 0.95 0.04 103.53 0.04 139.12 179.49 0.95\">\u003C/polygon>\n",[1736,36565,36566],{"class":1738,"line":2353},[1736,36567,36568],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F7B3CE\" fill-rule=\"nonzero\" points=\"234.04 0.95 226.67 0.95 0.04 175.45 0.04 211.38 234.04 31.2\">\u003C/polygon>\n",[1736,36570,36571],{"class":1738,"line":2358},[1736,36572,36573],{},"      \u003Cpolygon id=\"Shape\" fill=\"#88AEDC\" fill-rule=\"nonzero\" points=\"179.49 0.95 0.04 139.12 0.04 175.45 226.67 0.95\">\u003C/polygon>\n",[1736,36575,36576],{"class":1738,"line":2364},[1736,36577,36578],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F58F8E\" fill-rule=\"nonzero\" points=\"234.04 31.2 0.04 211.38 0.04 233.95 18.07 233.95 234.04 67.65\">\u003C/polygon>\n",[1736,36580,36581],{"class":1738,"line":2370},[1736,36582,36583],{},"      \u003Cpolygon id=\"Shape\" fill=\"#FEE18A\" fill-rule=\"nonzero\" points=\"234.04 67.65 18.07 233.95 64.7 233.95 234.04 103.56\">\u003C/polygon>\n",[1736,36585,36586],{"class":1738,"line":2376},[1736,36587,36588],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F3F095\" fill-rule=\"nonzero\" points=\"234.04 103.56 64.7 233.95 112.11 233.95 234.04 140.06\">\u003C/polygon>\n",[1736,36590,36591],{"class":1738,"line":2381},[1736,36592,36593],{},"      \u003Cpolygon id=\"Shape\" fill=\"#55C1AE\" fill-rule=\"nonzero\" points=\"234.04 140.08 112.13 233.95 158.35 233.95 234.04 175.67\">\u003C/polygon>\n",[1736,36595,36596],{"class":1738,"line":2387},[1736,36597,36598],{},"      \u003Cpolygon id=\"Shape\" fill=\"#F7B3CE\" fill-rule=\"nonzero\" points=\"234.04 212 205.53 233.95 234.04 233.95\">\u003C/polygon>\n",[1736,36600,36601],{"class":1738,"line":2393},[1736,36602,36603],{},"      \u003Cg id=\"Group\" transform=\"translate(37.000000, 77.000000)\" fill=\"#FFFFFF\">\n",[1736,36605,36606],{"class":1738,"line":2398},[1736,36607,36608],{},"        \u003Cpath d=\"M28.2371517,0.75 C32.7510836,1.7 36.0111455,3.55 39.371517,7.05 C42.4309598,10.25 44.3368421,13.9 45.1393189,18 C45.7913313,21.45 45.7913313,58.55 45.1393189,62.05 C43.4340557,71.15 35.6600619,78.25 26.0303406,79.5 C24.0241486,79.75 17.3034056,80 11.1845201,80 L-7.10542736e-15,80 L-7.10542736e-15,1.42108547e-14 L12.4383901,1.42108547e-14 C21.2656347,1.42108547e-14 25.7795666,0.2 28.2371517,0.75 Z M14.5448916,40 L14.5448916,65.6 L19.7108359,65.4 C24.174613,65.25 25.1275542,65.05 27.1337461,63.9 C31.0458204,61.6 31.0959752,61.45 31.0959752,39.7 C31.0959752,18.5 31.0959752,18.5 27.4346749,16.1 C25.6291022,14.9 24.8767802,14.75 19.9616099,14.55 L14.5448916,14.4 L14.5448916,40 Z\"\n",[1736,36610,36611],{"class":1738,"line":2404},[1736,36612,36613],{},"              id=\"Combined-Shape\">\u003C/path>\n",[1736,36615,36616],{"class":1738,"line":6959},[1736,36617,36618],{},"        \u003Cpath d=\"M93.7894737,7.25 L93.7894737,14.5 L68.2105263,14.5 L68.2105263,32.5 L83.7585139,32.5 L83.7585139,47 L68.2105263,47 L68.3108359,56.1 L68.4613003,65.25 L81.1504644,65.4 L93.7894737,65.5 L93.7894737,80 L78.993808,80 C62.5430341,80 59.9851393,79.7 57.3770898,77.4 C53.7157895,74.2 53.9164087,76.25 53.7659443,41.1 C53.6656347,19.2 53.8160991,8.85 54.1671827,7.45 C54.8693498,4.85 57.828483,1.65 60.4365325,0.75 C61.9913313,0.2 65.9034056,0.05 78.1411765,4.26325641e-14 L93.7894737,4.26325641e-14 L93.7894737,7.25 Z\"\n",[1736,36620,36621],{"class":1738,"line":7296},[1736,36622,36623],{},"              id=\"Path\">\u003C/path>\n",[1736,36625,36626],{"class":1738,"line":7305},[1736,36627,36628],{},"        \u003Cpath d=\"M125.437152,28.1 C129.148607,42.35 132.258204,53.7 132.358514,53.35 C132.508978,53 135.668731,40.95 139.430341,26.5 L146.301548,0.25 L154.125697,0.1 C160.043963,7.10542736e-15 162,0.15 162,0.6 C162,1.05 144.64644,66.8 143.643344,70.1 C142.941176,72.4 139.179567,77.1 137.073065,78.35 C134.414861,79.85 130.502786,80.1 128.095356,78.85 C125.9387,77.75 123.079876,74.45 121.625387,71.35 C120.722601,69.45 105.97709,15.35 102.566563,1.35 L102.21548,0 L110.039628,0 C117.713313,0 117.913932,0 118.31517,1.1 C118.515789,1.75 121.725697,13.9 125.437152,28.1 Z\"\n",[1736,36630,36631],{"class":1738,"line":7310},[1736,36632,36623],{},[1736,36634,36635],{"class":1738,"line":9659},[1736,36636,36637],{},"      \u003C/g>\n",[1736,36639,36640],{"class":1738,"line":9680},[1736,36641,36642],{},"    \u003C/g>\n",[1736,36644,36645],{"class":1738,"line":9699},[1736,36646,36647],{},"  \u003C/g>\n",[1736,36649,36650],{"class":1738,"line":9704},[1736,36651,36652],{},"\u003C/svg>\n",[11,36654,36655,36656,36661],{},"We certainly don't need access to everything in the SVG and don't intend on modifying it so how can we get a lighter version of this SVG? Have you heard of of ",[15,36657,36660],{"href":36658,"rel":36659},"https://jakearchibald.github.io/svgomg/",[19],"SVGOMG BY Jake Archibald",". I have been using it for years and it really is a great tool.",[11,36663,36664],{},[121,36665],{"alt":36666,"src":36667},"SVGOMG app showing open svg and paste markup options","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652107151/debbie.codes/blog/2022/svgomg-tool_2x_ilp1yt.png",[11,36669,36670],{},"You can either open the SVG directly or you can paste the SVG code and it will start converting the SVG to a lightweight version. You can choose what you want to remove and what you want to keep. Looking at our SVG you can see the difference in size with a saving of 71.73%.",[11,36672,36673],{},[121,36674],{"alt":36675,"src":36676},"dev logo in svgomg app showing what was removed and savings in size","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652107148/debbie.codes/blog/2022/dev-logo-svgomg_2x_ljbh5t.png",[11,36678,36679],{},"You can then download the file or just copy the code directly and paste it into the SVG. You can see below the difference in code and not only how it is minified but also how it removes all the unnecessary code we don't need.",[299,36681,36683],{"className":36504,"code":36682,"language":36506,"meta":307,"style":307},"\u003Csvg viewBox=\"0 0 235 234\" xmlns=\"http://www.w3.org/2000/svg\" class=\"rainbow-logo\" preserveAspectRatio=\"xMinYMin meet\">\u003Cg fill=\"none\" fill-rule=\"evenodd\">\u003Cpath fill=\"#88AEDC\" d=\"m234.04 175.67-75.69 58.28h47.18L234.04 212z\"/>\u003Cpath d=\"m234.04 140.06-121.93 93.89h.02l121.91-93.87zM133.25.95.04 103.51v.02L133.27.95z\"/>\u003Cpath fill=\"#F58F8E\" fill-rule=\"nonzero\" d=\"M.04.95v30.16L39.21.95z\"/>\u003Cpath fill=\"#FEE18A\" fill-rule=\"nonzero\" d=\"M39.21.95.04 31.11v35.9L85.84.95z\"/>\u003Cpath fill=\"#F3F095\" fill-rule=\"nonzero\" d=\"M85.84.95.04 67.01v36.5L133.25.95z\"/>\u003Cpath fill=\"#55C1AE\" fill-rule=\"nonzero\" d=\"M133.27.95.04 103.53v35.59L179.49.95z\"/>\u003Cpath fill=\"#F7B3CE\" fill-rule=\"nonzero\" d=\"M234.04.95h-7.37L.04 175.45v35.93l234-180.18z\"/>\u003Cpath fill=\"#88AEDC\" fill-rule=\"nonzero\" d=\"M179.49.95.04 139.12v36.33L226.67.95z\"/>\u003Cpath fill=\"#F58F8E\" fill-rule=\"nonzero\" d=\"M234.04 31.2.04 211.38v22.57h18.03l215.97-166.3z\"/>\u003Cpath fill=\"#FEE18A\" fill-rule=\"nonzero\" d=\"M234.04 67.65 18.07 233.95H64.7l169.34-130.39z\"/>\u003Cpath fill=\"#F3F095\" fill-rule=\"nonzero\" d=\"M234.04 103.56 64.7 233.95h47.41l121.93-93.89z\"/>\u003Cpath fill=\"#55C1AE\" fill-rule=\"nonzero\" d=\"m234.04 140.08-121.91 93.87h46.22l75.69-58.28z\"/>\u003Cpath fill=\"#F7B3CE\" fill-rule=\"nonzero\" d=\"m234.04 212-28.51 21.95h28.51z\"/>\u003Cg fill=\"#FFF\">\u003Cpath d=\"M65.237 77.75c4.514.95 7.774 2.8 11.135 6.3 3.059 3.2 4.965 6.85 5.767 10.95.652 3.45.652 40.55 0 44.05-1.705 9.1-9.479 16.2-19.109 17.45-2.006.25-8.727.5-14.845.5H37V77h12.438c8.828 0 13.342.2 15.8.75ZM51.545 117v25.6l5.166-.2c4.464-.15 5.417-.35 7.423-1.5 3.912-2.3 3.962-2.45 3.962-24.2 0-21.2 0-21.2-3.661-23.6-1.806-1.2-2.558-1.35-7.473-1.55l-5.417-.15V117ZM130.79 84.25v7.25h-25.58v18h15.549V124H105.21l.1 9.1.15 9.15 12.69.15 12.638.1V157h-14.795c-16.451 0-19.009-.3-21.617-2.6-3.661-3.2-3.46-1.15-3.611-36.3-.1-21.9.05-32.25.401-33.65.702-2.6 3.661-5.8 6.27-6.7 1.554-.55 5.466-.7 17.704-.75h15.648v7.25ZM162.437 105.1c3.712 14.25 6.821 25.6 6.922 25.25.15-.35 3.31-12.4 7.071-26.85l6.872-26.25 7.824-.15c5.918-.1 7.874.05 7.874.5s-17.354 66.2-18.357 69.5c-.702 2.3-4.463 7-6.57 8.25-2.658 1.5-6.57 1.75-8.978.5-2.156-1.1-5.015-4.4-6.47-7.5-.902-1.9-15.648-56-19.058-70l-.352-1.35h7.825c7.673 0 7.874 0 8.275 1.1.2.65 3.41 12.8 7.122 27Z\"/>\u003C/g>\u003C/g>\u003C/svg>\n",[179,36684,36685],{"__ignoreMap":307},[1736,36686,36687],{"class":1738,"line":1739},[1736,36688,36682],{},[11,36690,36691,36692,36695],{},"Our SVG is now much lighter improving the performance of our website. I highly encourage you to give this tool, ",[15,36693,36660],{"href":36658,"rel":36694},[19],", a try.",[2011,36697,36698],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":36700},[],"When working with SVG's sometimes we just put the svg into our code and think nothing more of it. But do we always need all that code that the SVG gives us? Perhaps not. SVGO is a tool that can take an SVG and compress it down to a smaller size ensuring your site is more performant.","v1652107148/debbie.codes/blog/2022/dev-logo-svgomg_2x_ljbh5t.png",{"ogImage":36704,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1652107148/debbie.codes/blog/2022/dev-logo-svgomg_2x_ljbh5t.png","/blog/minimizing-svgs",{"title":36407,"description":36701},"blog/minimizing-svgs",[36709],"performance","dYoMfNWTDuzs4vs8e98Z6ukaxrZp0v-VkrfpZszJwo0",{"id":36712,"title":36713,"body":36714,"canonical":788,"date":36718,"description":36719,"extension":786,"featured":787,"image":36720,"meta":36721,"navigation":790,"ogimage":788,"path":36722,"provider":3460,"published":787,"seo":36723,"stem":36724,"tags":36725,"url":36726,"__hash__":36727},"blog/blog/moving-from-dontenv-to-runtime-config.md","Moving from @nuxtjs/dotenv to runtime config",{"type":8,"value":36715,"toc":36716},[],{"title":307,"searchDepth":748,"depth":748,"links":36717},[],"2020-06-15","In our frontend applications, we often use APIs and third-party integrations which require us to use configuration data which is usually provided by environment variables. These variables should not be exposed to the frontend as the browser environment is accessible by all visitors.","v1630862642/debbie.codes/featured-posts/moving-from-dot-env_kn8gra",{"platform":20935},"/blog/moving-from-dontenv-to-runtime-config",{"title":36713,"description":36719},"blog/moving-from-dontenv-to-runtime-config",[5239],"https://nuxtjs.org/blog/moving-from-nuxtjs-dotenv-to-runtime-config","aX171kVKooeiWwcSTJZSJdkMlDKB_dBzCJcfdlFsEE4",{"id":36729,"title":36730,"body":36731,"canonical":788,"date":36792,"description":36793,"extension":786,"featured":787,"image":36794,"meta":36795,"navigation":790,"ogimage":788,"path":36797,"provider":3460,"published":787,"seo":36798,"stem":36799,"tags":36800,"url":788,"__hash__":36801},"blog/blog/not-one-but-two.md","Not One but Two",{"type":8,"value":36732,"toc":36787},[36733,36736,36740,36743,36746,36749,36752,36756,36759,36762,36765,36768,36771,36775,36778,36781,36784],[11,36734,36735],{},"On our 9th attempt at fertility treatment, we were super excited when the doctor called us 10 days after the transfer to tell us that we were pregnant. It had worked. We had done the blood test that morning and then gone for ice cream. We had just left the coffee shop and were in the street on a busy road about to cross when he rang. I asked him if he was sure and he said \"yes, very sure, High levels meaning it's a healthy pregnancy\". My husband cried; I was still in disbelief. We went to the department store and walked through the children's floor but then felt we were tempting fate and decided to turn back. It was still way too early, and anything could go wrong.",[23,36737,36739],{"id":36738},"traveling-and-running","Traveling and Running",[11,36741,36742],{},"I had a scan programmed on my 7th week, after my week of traveling and speaking at conferences. I was advised to hydrate well on the long flights and to take my medication at the correct time, considering time zone differences. I had to take injections which helped with possible blood clotting so this now had to be taken at 1pm rather than 10pm. That was a bit inconvenient, but my husband sent me reminders and I managed to find a quiet place each day where I could take the injection while in the middle of a conference.",[11,36744,36745],{},"While at both conferences I felt great. I was out running almost every day but I was monitoring my heart rate, which was super high, so I slowed down the runs and kept them short. I was constantly hungry but did manage to make sure I had enough protein bars with me. I was so super busy that I managed to not have any problems with tiredness. I got through the day but by 9:30 every night I was in bed and fast asleep till 7am. As for symptoms, I only really had my chest which had grown and was super sensitive and hurt while running but I just got used to it and after a few minutes it was fine. Hugs were the worst. Big bear hugs were so soar. But other than that, I felt great.",[11,36747,36748],{},"On the way home at the airport, I had my first bout of nausea. I was sitting in the airport lounge after the long flight. Breakfast was terrible on the plane, so I didn't eat it. I had some beans on gluten free bread in the lounge and felt quite full. I then sat down and watched some YouTube videos of trail running but then I started to feel real nauseous. I couldn't look at the tablet and so I put on some relaxing music and tried to just breathe. But then I knew what was coming and had to run as fast as I could and luckily managed to get to the bathroom on time. Was this morning sickness and was this going to be my new normal? No idea but oh boy it is not fun when you are traveling.",[11,36750,36751],{},"I arrived home on Saturday night and spent Sunday relaxing and catching up on sleep. However on Sunday I had no morning sickness and started to think maybe there is something wrong. I had my first scan scheduled for Monday morning but worry and doubt ran though my mind and I started googling like hell for all the things that could be going wrong. I didn't sleep at all on the Sunday night and I was super nervous that something wasn't right.",[23,36753,36755],{"id":36754},"first-scan","First Scan",[11,36757,36758],{},"When we arrived at the clinic I told them how worried I was but they told me it was normal to feel this way. They asked about symptoms and said it all sounded good. Then came the ultrasound. The great thing about this clinic is that as you lie on the bed the doctor has a small computer screen to see everything but you have a big projected image of what he sees and its kinda like your insides are being shown on a big cinema screen but it means its really easy to see things both for me and my husband.",[11,36760,36761],{},"As he did the scan I saw two circles and just thought, \"oh gosh\". The doctor started explaining things and said so here we see two sacs. He even started doubting himself and asked me to confirm that we only transferred 1 embryo. Transferring more than one is considered unsafe and not recommended so they hardly ever do it. But we only had one transferred. Yet right there on the screen there were two. So he said this is very unexpected but you have two babies. He said if we look at the scan this way you can only see one but if I turn it round you can clearly see two. He measured both and they were both the same size. He said they are identical twins and therefore of same sex. The embryo had split into two after implantation so they have their own sac but share the same placenta. He said the odds of identical twins were 1 in 250, so not too common.",[11,36763,36764],{},"I did not know what to think except for worry, was it safe and how was I going to get two babies out of me, it was hard enough thinking of getting one out. My husband on the other hand was ecstatic and was even taking videos of the heartbeats. I was too much in shock to even take it all in and was so not prepared for this news but then again neither was the doctor. He did say that it explained why the blood test results were so high but it's never a sure sign of a twin pregnancy.",[11,36766,36767],{},"The doctor told me I couldn't run anymore and had to remove all high impact activities. This was not fun to hear as I love my runs and playing padel and on a singleton pregnancy it would have been fine to keep running but with twins I need to be more careful with everything which was really annoying to hear. Although they did tell me that I still should do sport and stay as active as possible but just low impact sports only so walking, swimming, yoga, weights or cycling on a stationary bike.",[11,36769,36770],{},"The nurses were all super excited and in the hallway came running up to me to congratulate me, double congratulations they kept saying. I was still trying to take it all in and really didn't know how I felt or how I should be feeling. We were told that as of today both babies are healthy and everything looks great but it was still very early days and we still had a long way to go before we could share our news with the world.",[23,36772,36774],{"id":36773},"taking-it-all-in","Taking it all in",[11,36776,36777],{},"We took some time to just go for ice cream and chat about it. My husband thought it was the best news ever but I had so many things going on in my head. How could I get on a plane with 2 babies? Was I now dependent on another adult for the rest of my life? It seems stupid now but it's a massive life change and something you are just unprepared for so there are so many questions left unanswered. We then went for a walk though the childrens floor of the department store and just realized how little focus there is on twins. For example so many buggies but only 1 that could work for twins. We suddenly realised we need two of everything from car seats to cots. And it was all very overwhelming indeed.",[11,36779,36780],{},"I went home and spent the day on Google just googling anything and everything as my head tried to take in the news of how my life would change and how worrying it was be be having twins, delivering early, possibilities of premature babies, twin to twin transfusion and so many causes for concern. It is so not healthy to google everything but I managed to find a good book and podcast which helped a lot at first but then just scared me more hearing all the horror stories. It took about 10 hours before I felt I could call someone to tell them the news and actually say it out load although I still wasn't a hundred percent convinced this was happening and perhaps it had all been a dream.",[11,36782,36783],{},"The news got easier to deal with by the next day and a call with an online fertility nurse really helped. When it is so early on in the pregnancy you can't go to that many people for advise cause you shouldn't really tell anyone so you keep everything to yourself and that's a lot of doubts and emotions. Having people on your side who know what you are going through is really helpful at this stage so then when people ask you the questions you are asking yourself you will be able to give them answers and will feel more in control of the situation.",[11,36785,36786],{},"Deep down it is of course great news. Not 1 but 2 babies is more than we could have hoped for and it means our children will have someone to play with, someone to bond with and it really will be a unique experience for us and for the family. Our quiet house will be filled with laughter and it will be crazy busy but crazy fun.  I don't know how I am going to fit things in the suitcase when I visit family at Christmas times but I guess if I can focus only on worrying about silly things like that then I can get through this and hopefully everything will work out with no problems. So many things out of our control. So many things we just have to let go and have faith that things will be ok and if not we just have to deal with them if and when they arise. Wish me luck, I think I'm gonna need it.",{"title":307,"searchDepth":748,"depth":748,"links":36788},[36789,36790,36791],{"id":36738,"depth":748,"text":36739},{"id":36754,"depth":748,"text":36755},{"id":36773,"depth":748,"text":36774},"2023-08-11","On our 9th attempt at fertility treatment, we were super excited when the doctor called us 10 days after the transfer to tell us that we were pregnant. It had worked. I asked him if he was sure and he said \"yes, very sure. But then when we had the first scan, we got a big surprise.","v1691829487/debbie.codes/blog/2023/scan-twins-2_iwmfs3.jpg",{"ogImage":36796},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651559990/debbie.codes/blog/2023/scan-twins-2_iwmfs3.jpg","/blog/not-one-but-two",{"title":36730,"description":36793},"blog/not-one-but-two",[3464],"cYYEh1R2h_zXagTw9Uxe4IWDo8NIlVRfVs5NlYbWXcg",{"id":36803,"title":36804,"body":36805,"canonical":788,"date":37027,"description":37028,"extension":786,"featured":787,"image":37029,"meta":37030,"navigation":790,"ogimage":788,"path":37031,"provider":5235,"published":787,"seo":37032,"stem":37033,"tags":37034,"url":788,"__hash__":37035},"blog/blog/nuxt-active-styles.md","Styling your active classes in Nuxt.js",{"type":8,"value":36806,"toc":37025},[36807,36817,36822,36828,36856,36862,36870,36897,36906,36909,36922,36956,36966,37002,37008,37011,37014,37022],[11,36808,36809,36810,36813,36814,891],{},"Nuxt.js, which uses ",[179,36811,36812],{},"vue-router",", can easily tell what route you are on and therefore can add a class to that link when the user is on that page. By default this class is called ",[179,36815,36816],{},"nuxt-link-active",[11,36818,36819],{},[121,36820],{"alt":36816,"src":36821},"https://user-images.githubusercontent.com/13063165/84566699-9328f280-ad73-11ea-8697-d1e08b5bd0f2.png",[11,36823,36824,36825,36827],{},"In order to style your active links the only thing you have to do to is add the ",[179,36826,36816],{}," class to your styles and then you can style it as you wish.",[299,36829,36831],{"className":4857,"code":36830,"language":4859,"meta":307,"style":307},".nuxt-link-active {\n  color: red;\n}\n",[179,36832,36833,36840,36852],{"__ignoreMap":307},[1736,36834,36835,36838],{"class":1738,"line":1739},[1736,36836,36837],{"class":2674},".nuxt-link-active",[1736,36839,4914],{"class":1912},[1736,36841,36842,36845,36847,36850],{"class":1738,"line":748},[1736,36843,36844],{"class":1918},"  color",[1736,36846,3065],{"class":1912},[1736,36848,36849],{"class":1918},"red",[1736,36851,7682],{"class":1912},[1736,36853,36854],{"class":1738,"line":756},[1736,36855,1976],{"class":1912},[11,36857,36858,36859,891],{},"Sometimes this will also add styles to other links such as the parent routes or the home page. In order to fix this we use the exact active class. By default this class is called ",[179,36860,36861],{},"nuxt-link-exact-active",[11,36863,36864,36867,36868,36827],{},[121,36865],{"alt":36861,"src":36866},"https://user-images.githubusercontent.com/13063165/84566719-b6ec3880-ad73-11ea-9a5d-47e7e23de987.png"," The only thing you have to do to is add the ",[179,36869,36861],{},[299,36871,36873],{"className":4857,"code":36872,"language":4859,"meta":307,"style":307},".nuxt-link-exact-active {\n  color: green;\n}\n",[179,36874,36875,36882,36893],{"__ignoreMap":307},[1736,36876,36877,36880],{"class":1738,"line":1739},[1736,36878,36879],{"class":2674},".nuxt-link-exact-active",[1736,36881,4914],{"class":1912},[1736,36883,36884,36886,36888,36891],{"class":1738,"line":748},[1736,36885,36844],{"class":1918},[1736,36887,3065],{"class":1912},[1736,36889,36890],{"class":1918},"green",[1736,36892,7682],{"class":1912},[1736,36894,36895],{"class":1738,"line":756},[1736,36896,1976],{"class":1912},[11,36898,36899,36900,36905],{},"You can add this css to your navigation component or to a specific page or layout or in your global css file. See the ",[15,36901,36904],{"href":36902,"rel":36903},"https://nuxtjs.org/api/configuration-css",[19],"nuxt docs"," for more information on adding global css files.",[11,36907,36908],{},"Should you want to you can also configure the classname to be something different. Although this is not necessary for it to work you might want to configure the classname to be a class that is already in your third party styles or configured in tailwind for example.",[11,36910,36911,36912,36914,36915,36918,36919,891],{},"You can change the active class in your ",[179,36913,33252],{}," file using the ",[179,36916,36917],{},"router"," property and setting a value to ",[179,36920,36921],{},"linkActiveClass",[299,36923,36925],{"className":5635,"code":36924,"language":5637,"meta":307,"style":307},"export default {\n  router: {\n    linkActiveClass: 'my-custom-exact-active-link'\n  }\n}\n",[179,36926,36927,36935,36940,36948,36952],{"__ignoreMap":307},[1736,36928,36929,36931,36933],{"class":1738,"line":1739},[1736,36930,6632],{"class":4866},[1736,36932,30438],{"class":4866},[1736,36934,4914],{"class":1912},[1736,36936,36937],{"class":1738,"line":748},[1736,36938,36939],{"class":1912},"  router: {\n",[1736,36941,36942,36945],{"class":1738,"line":756},[1736,36943,36944],{"class":1912},"    linkActiveClass: ",[1736,36946,36947],{"class":1935},"'my-custom-exact-active-link'\n",[1736,36949,36950],{"class":1738,"line":1755},[1736,36951,1971],{"class":1912},[1736,36953,36954],{"class":1738,"line":1761},[1736,36955,1976],{"class":1912},[11,36957,36958,36959,36914,36961,36918,36963,891],{},"You can change the exact active class in your ",[179,36960,33252],{},[179,36962,36917],{},[179,36964,36965],{},"linkExactActiveClass",[299,36967,36969],{"className":5635,"code":36968,"language":5637,"meta":307,"style":307},"export default {\n  router: {\n    linkExactActiveClass: 'text-primary' // tailwind class with custom color\n  }\n}\n",[179,36970,36971,36979,36983,36994,36998],{"__ignoreMap":307},[1736,36972,36973,36975,36977],{"class":1738,"line":1739},[1736,36974,6632],{"class":4866},[1736,36976,30438],{"class":4866},[1736,36978,4914],{"class":1912},[1736,36980,36981],{"class":1738,"line":748},[1736,36982,36939],{"class":1912},[1736,36984,36985,36988,36991],{"class":1738,"line":756},[1736,36986,36987],{"class":1912},"    linkExactActiveClass: ",[1736,36989,36990],{"class":1935},"'text-primary'",[1736,36992,36993],{"class":6820}," // tailwind class with custom color\n",[1736,36995,36996],{"class":1738,"line":1755},[1736,36997,1971],{"class":1912},[1736,36999,37000],{"class":1738,"line":1761},[1736,37001,1976],{"class":1912},[11,37003,37004],{},[121,37005],{"alt":37006,"src":37007},"nuxt-link-active text-primary","https://user-images.githubusercontent.com/13063165/84566760-f2870280-ad73-11ea-8173-826ad4572478.png",[11,37009,37010],{},"Although adding your tailwind class to the config is a great method it will cause you problems if you don't want all your active links across all pages to be that colour. If you want more control then you should use the default class name or a custom one and use scoped styling.",[11,37012,37013],{},"And that is all there is to it. If you don't already have your active links styled then go ahead and add a class for them.",[11,37015,37016,37017,37021],{},"See the ",[15,37018,36904],{"href":37019,"rel":37020},"https://nuxtjs.org/api/configuration-router#linkactiveclass",[19]," for more info on the active and exact active classes.",[2011,37023,37024],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":37026},[],"2020-06-28","Nuxt.js, which uses `vue-router`, can easily tell what route you are on and therefore can add a class to that link when the user is on that page. By default this class is called `nuxt-link-active`.","photo-1428790067070-0ebf4418d9d8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-active-styles",{"title":36804,"description":37028},"blog/nuxt-active-styles",[5239],"hxJJwIp1y4dlp3vQjDkOT42ceys81vYvme9dluOmFIU",{"id":37037,"title":37038,"body":37039,"canonical":788,"date":37277,"description":37278,"extension":786,"featured":787,"image":37279,"meta":37280,"navigation":790,"ogimage":788,"path":37282,"provider":3460,"published":787,"seo":37283,"stem":37284,"tags":37285,"url":788,"__hash__":37286},"blog/blog/nuxt-add-vue-plugins.md","Adding Vue Plugins to your Nuxt APP",{"type":8,"value":37040,"toc":37268},[37041,37050,37054,37057,37071,37075,37078,37123,37127,37132,37149,37153,37166,37182,37186,37189,37212,37218,37225,37229,37242,37244,37265],[11,37042,37043,37044,37049],{},"Let's use the ",[15,37045,37048],{"href":37046,"rel":37047},"https://github.com/Akryum/v-tooltip",[19],"Vue Tooltip plugin"," and add it to our Nuxt app so we can have amazing tooltips.",[23,37051,37053],{"id":37052},"install-the-npm-package","Install the npm package",[11,37055,37056],{},"For this example i am using the latest version of the library. We can install it either using npm or yarn.",[299,37058,37060],{"className":2665,"code":37059,"language":2667,"meta":307,"style":307},"yarn add v-tooltip@v2.1.2\n",[179,37061,37062],{"__ignoreMap":307},[1736,37063,37064,37066,37068],{"class":1738,"line":1739},[1736,37065,6575],{"class":2674},[1736,37067,2681],{"class":1935},[1736,37069,37070],{"class":1935}," v-tooltip@v2.1.2\n",[23,37072,37074],{"id":37073},"create-a-nuxt-plugin","Create a Nuxt plugin",[11,37076,37077],{},"In order to use Vue plugins we need to create a Nuxt plugin and we do this by creating a .js file in the /plugins folder. We then paste in the code that we need to use the plugin. We import Vue and VTooltip and then tell Vue to use the VTooltip.",[299,37079,37082],{"className":8734,"code":37080,"filename":37081,"language":8736,"meta":307,"style":307},"import Vue from 'vue'\nimport VTooltip from 'v-tooltip'\n\nVue.use(VTooltip)\n","plugins/v-tooltip.js",[179,37083,37084,37096,37108,37112],{"__ignoreMap":307},[1736,37085,37086,37088,37091,37093],{"class":1738,"line":1739},[1736,37087,4996],{"class":4866},[1736,37089,37090],{"class":1912}," Vue ",[1736,37092,5002],{"class":4866},[1736,37094,37095],{"class":1935}," 'vue'\n",[1736,37097,37098,37100,37103,37105],{"class":1738,"line":748},[1736,37099,4996],{"class":4866},[1736,37101,37102],{"class":1912}," VTooltip ",[1736,37104,5002],{"class":4866},[1736,37106,37107],{"class":1935}," 'v-tooltip'\n",[1736,37109,37110],{"class":1738,"line":756},[1736,37111,1747],{"emptyLinePlaceholder":790},[1736,37113,37114,37117,37120],{"class":1738,"line":1755},[1736,37115,37116],{"class":1912},"Vue.",[1736,37118,37119],{"class":2674},"use",[1736,37121,37122],{"class":1912},"(VTooltip)\n",[23,37124,37126],{"id":37125},"register-the-plugin","Register the plugin",[11,37128,37129,37130,2900],{},"Nuxt doesn't automatically register plugins therefore we need to tell Nuxt that the plugin exists. We do this by adding it to the plugins array in our ",[179,37131,33252],{},[299,37133,37135],{"className":8734,"code":37134,"filename":33252,"language":8736,"meta":307,"style":307},"plugins: ['~/plugins/v-tooltip.js']\n",[179,37136,37137],{"__ignoreMap":307},[1736,37138,37139,37142,37144,37147],{"class":1738,"line":1739},[1736,37140,37141],{"class":2674},"plugins",[1736,37143,16439],{"class":1912},[1736,37145,37146],{"class":1935},"'~/plugins/v-tooltip.js'",[1736,37148,8420],{"class":1912},[23,37150,37152],{"id":37151},"lets-add-some-styles","Let's add some styles",[11,37154,37155,37156,37161,37162,37165],{},"You can add styles how you wish but the plugin gives us some ",[15,37157,37160],{"href":37158,"rel":37159},"https://floating-vue.starpad.dev/legacy/v2/#css",[19],"styles that we can copy"," directly and add to a ",[179,37163,37164],{},".css"," file which we can put in the assets folder. Then we just have to register the css file by adding it to our Nuxt config.",[299,37167,37169],{"className":8734,"code":37168,"filename":33252,"language":8736,"meta":307,"style":307},"css: ['~/assets/v-tooltip.css']\n",[179,37170,37171],{"__ignoreMap":307},[1736,37172,37173,37175,37177,37180],{"class":1738,"line":1739},[1736,37174,4859],{"class":2674},[1736,37176,16439],{"class":1912},[1736,37178,37179],{"class":1935},"'~/assets/v-tooltip.css'",[1736,37181,8420],{"class":1912},[23,37183,37185],{"id":37184},"use-the-plugin","Use the Plugin",[11,37187,37188],{},"We can now use the plugin in any of our layouts, pages or components by using the tooltip directive. If we add it to the logo then every time someone hovers over our Logo they will get the tooltip with a message saying 'Nuxt is Awesome'",[299,37190,37193],{"className":21974,"code":37191,"filename":37192,"language":21976,"meta":307,"style":307},"\u003CLogo v-tooltip=\"Nuxt is Awesome\" />\n","pages/index.vue",[179,37194,37195],{"__ignoreMap":307},[1736,37196,37197,37199,37202,37205,37207,37210],{"class":1738,"line":1739},[1736,37198,6657],{"class":1912},[1736,37200,37201],{"class":30490},"Logo",[1736,37203,37204],{"class":2674}," v-tooltip",[1736,37206,5062],{"class":1912},[1736,37208,37209],{"class":1935},"\"Nuxt is Awesome\"",[1736,37211,6739],{"class":1912},[11,37213,37214],{},[121,37215],{"alt":37216,"src":37217},"Vue plugin in use","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto,w_1200/v1612282619/debbie.codes/blog/vue-plugins_ubadyf.png",[11,37219,37220,37221,891],{},"And that's it. The library itself allows you to do lots of cool things including dynamic messages, add components inside and so much more. Do check out the plugins docs and play around with the ",[15,37222,37224],{"href":37223},"(https://github.com/Akryum/v-tooltip)","library",[23,37226,37228],{"id":37227},"adding-client-side-plugins","Adding Client Side Plugins",[11,37230,37231,37232,37235,37236,37241],{},"if you do use a library that needs access to browser elements such as window or document then you will need to register the plugin only on the client side. This can be done by adding ",[179,37233,37234],{},".client.js"," as the file extension to your plugin. This tells Nuxt to not render this plugin on the Server but only on the Client, in the Browser. See the ",[15,37237,37240],{"href":37238,"rel":37239},"https://nuxtjs.org/docs/2.x/directory-structure/plugins#vue-plugins",[19],"Nuxt docs"," for more details.",[23,37243,5706],{"id":5705},[70,37245,37246,37252,37258],{},[73,37247,37248],{},[15,37249,37251],{"href":37046,"rel":37250},[19],"Vue Tooltip Plugin",[73,37253,37254],{},[15,37255,37257],{"href":37238,"rel":37256},[19],"Nuxt Vue Plugin Docs",[73,37259,37260],{},[15,37261,37264],{"href":37262,"rel":37263},"https://nuxtjs.org/examples/plugins-vue",[19],"Nuxt Vue Plugin Example",[2011,37266,37267],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":37269},[37270,37271,37272,37273,37274,37275,37276],{"id":37052,"depth":748,"text":37053},{"id":37073,"depth":748,"text":37074},{"id":37125,"depth":748,"text":37126},{"id":37151,"depth":748,"text":37152},{"id":37184,"depth":748,"text":37185},{"id":37227,"depth":748,"text":37228},{"id":5705,"depth":748,"text":5706},"2021-02-02","In Nuxt we can add Vue plugins to our application by creating a Nuxt plugin and then registering that plugin in the Nuxt Config file.","c_scale,fl_lossy,f_auto,q_auto/v1612282619/debbie.codes/blog/vue-plugins_ubadyf",{"video":37281},"gAqcW51NwTE","/blog/nuxt-add-vue-plugins",{"title":37038,"description":37278},"blog/nuxt-add-vue-plugins",[5239],"o4SQcQSa6cXsZxmrw1HG1D6XrIvGtBvoKWy7v7bjPSM",{"id":37288,"title":37289,"body":37290,"canonical":788,"date":37027,"description":37531,"extension":786,"featured":787,"image":37532,"meta":37533,"navigation":790,"ogimage":788,"path":37534,"provider":5235,"published":787,"seo":37535,"stem":37536,"tags":37537,"url":788,"__hash__":37538},"blog/blog/nuxt-adding-pwa.md","Adding a PWA in Nuxt.js",{"type":8,"value":37291,"toc":37529},[37292,37295,37298,37312,37315,37319,37346,37351,37376,37387,37400,37403,37422,37518,37526],[11,37293,37294],{},"Most people don't realise how easy it really is to add a PWA with Nuxt.js. Progressive Web Apps (PWA) deliver native-like capabilities, reliability, and installability while reaching anyone, anywhere, on any device with a single codebase. You can turn your website into a PWA which will give you the benefits of better offline support as well as that app like look and feel. Just save it to the home screen and open it from there and you will see how amazing it is.",[11,37296,37297],{},"The Nuxt.js PWA module registers a service worker for you to deal with offline caching.",[70,37299,37300,37303,37306,37309],{},[73,37301,37302],{},"It automatically generates a manifest.json file",[73,37304,37305],{},"It automatically adds SEO friendly meta data with manifest integration",[73,37307,37308],{},"It automatically generates app icons with different sizes",[73,37310,37311],{},"And you can even set up free push notifications using OneSignal",[11,37313,37314],{},"Ok so there is a lot of automatic stuff so what do you need to do exactly?",[2260,37316,37317],{},[73,37318,37053],{},[299,37320,37322],{"className":2665,"code":37321,"language":2667,"meta":307,"style":307},"yarn add @nuxtjs/pwa\nor\nnpm i @nuxtjs/pwa\n",[179,37323,37324,37333,37338],{"__ignoreMap":307},[1736,37325,37326,37328,37330],{"class":1738,"line":1739},[1736,37327,6575],{"class":2674},[1736,37329,2681],{"class":1935},[1736,37331,37332],{"class":1935}," @nuxtjs/pwa\n",[1736,37334,37335],{"class":1738,"line":748},[1736,37336,37337],{"class":2674},"or\n",[1736,37339,37340,37342,37344],{"class":1738,"line":756},[1736,37341,6565],{"class":2674},[1736,37343,6568],{"class":1935},[1736,37345,37332],{"class":1935},[2260,37347,37348],{"start":748},[73,37349,37350],{},"Add the module to your nuxt.config.js file",[299,37352,37354],{"className":5635,"code":37353,"language":5637,"meta":307,"style":307},"{\n  modules: ['@nuxtjs/pwa']\n}\n",[179,37355,37356,37360,37372],{"__ignoreMap":307},[1736,37357,37358],{"class":1738,"line":1739},[1736,37359,1913],{"class":1912},[1736,37361,37362,37365,37367,37370],{"class":1738,"line":748},[1736,37363,37364],{"class":2674},"  modules",[1736,37366,16439],{"class":1912},[1736,37368,37369],{"class":1935},"'@nuxtjs/pwa'",[1736,37371,8420],{"class":1912},[1736,37373,37374],{"class":1738,"line":756},[1736,37375,1976],{"class":1912},[2260,37377,37378,37384],{"start":756},[73,37379,37380,37381,37383],{},"Add an icon.png file to your ",[179,37382,36276],{}," directory. It should be square and be at least 512px x 512px.",[73,37385,37386],{},"In your .gitignore file make sure you ignore the service worker file.",[299,37388,37390],{"className":2294,"code":37389,"language":2296,"meta":307,"style":307},"sw.\\*\n",[179,37391,37392],{"__ignoreMap":307},[1736,37393,37394,37397],{"class":1738,"line":1739},[1736,37395,37396],{"class":1912},"sw.",[1736,37398,37399],{"class":1918},"\\*\n",[11,37401,37402],{},"And that's it you now have a PWA up and running.",[11,37404,37405,37406,37408,37409,37412,37413,37415,37416,10141,37419,34819],{},"You can also further customise the PWA by modifying the title and author or name for example. The module by default adds the ",[179,37407,17364],{}," name and author but you can modify this by creating a ",[179,37410,37411],{},"pwa"," key in your ",[179,37414,33252],{}," file and modifying the ",[179,37417,37418],{},"meta",[179,37420,37421],{},"manifest",[299,37423,37425],{"className":5635,"code":37424,"language":5637,"meta":307,"style":307},"pwa: {\n  meta: {\n    title: 'My PWA',\n    author: 'Me',\n  },\n  manifest: {\n    name: 'Nuxt.js PWAs are so easy',\n    short_name: 'Nuxt.js PWA',\n    lang: 'en',\n  },\n}\n",[179,37426,37427,37433,37440,37452,37464,37468,37475,37487,37499,37510,37514],{"__ignoreMap":307},[1736,37428,37429,37431],{"class":1738,"line":1739},[1736,37430,37411],{"class":2674},[1736,37432,1922],{"class":1912},[1736,37434,37435,37438],{"class":1738,"line":748},[1736,37436,37437],{"class":2674},"  meta",[1736,37439,1922],{"class":1912},[1736,37441,37442,37445,37447,37450],{"class":1738,"line":756},[1736,37443,37444],{"class":2674},"    title",[1736,37446,3065],{"class":1912},[1736,37448,37449],{"class":1935},"'My PWA'",[1736,37451,1939],{"class":1912},[1736,37453,37454,37457,37459,37462],{"class":1738,"line":1755},[1736,37455,37456],{"class":2674},"    author",[1736,37458,3065],{"class":1912},[1736,37460,37461],{"class":1935},"'Me'",[1736,37463,1939],{"class":1912},[1736,37465,37466],{"class":1738,"line":1761},[1736,37467,4929],{"class":1912},[1736,37469,37470,37473],{"class":1738,"line":1767},[1736,37471,37472],{"class":2674},"  manifest",[1736,37474,1922],{"class":1912},[1736,37476,37477,37480,37482,37485],{"class":1738,"line":1772},[1736,37478,37479],{"class":2674},"    name",[1736,37481,3065],{"class":1912},[1736,37483,37484],{"class":1935},"'Nuxt.js PWAs are so easy'",[1736,37486,1939],{"class":1912},[1736,37488,37489,37492,37494,37497],{"class":1738,"line":1778},[1736,37490,37491],{"class":2674},"    short_name",[1736,37493,3065],{"class":1912},[1736,37495,37496],{"class":1935},"'Nuxt.js PWA'",[1736,37498,1939],{"class":1912},[1736,37500,37501,37504,37506,37508],{"class":1738,"line":1784},[1736,37502,37503],{"class":2674},"    lang",[1736,37505,3065],{"class":1912},[1736,37507,21941],{"class":1935},[1736,37509,1939],{"class":1912},[1736,37511,37512],{"class":1738,"line":1790},[1736,37513,4929],{"class":1912},[1736,37515,37516],{"class":1738,"line":1796},[1736,37517,1976],{"class":1912},[11,37519,37520,37521],{},"For a full list of meta options for your pwa see ",[15,37522,37525],{"href":37523,"rel":37524},"https://pwa.nuxtjs.org/modules/meta.html#mobileappios",[19],"the Nuxt PWA docs",[2011,37527,37528],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":37530},[],"Most people don't realise how easy it really is to add a PWA with Nuxt.js. Progressive Web Apps (PWA) deliver native-like capabilities, reliability, and installability while reaching anyone, anywhere, on any device with a single codebase.","photo-1511804074-5e57bc14db9f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-adding-pwa",{"title":37289,"description":37531},"blog/nuxt-adding-pwa",[5239,36709],"IgVq94G3VvEaxI1i_x2yGQM4twO9SoKjIbZuUhnU_B0",{"id":37540,"title":37541,"body":37542,"canonical":788,"date":37751,"description":37752,"extension":786,"featured":787,"image":37753,"meta":37754,"navigation":790,"ogimage":788,"path":37755,"provider":3460,"published":787,"seo":37756,"stem":37757,"tags":37758,"url":788,"__hash__":37759},"blog/blog/nuxt-analytics.md","Adding analytics to your Nuxt site",{"type":8,"value":37543,"toc":37742},[37544,37553,37556,37560,37570,37573,37577,37585,37589,37596,37600,37611,37615,37622,37626,37634,37648,37658,37685,37695,37729,37733,37736,37739],[11,37545,37546,37547,37552],{},"I recently just added ",[15,37548,37551],{"href":37549,"rel":37550},"https://plausible.io/",[19],"Plausible"," to my personal Nuxt site. Although I am still running the free trial version I am pretty sure that I will pay the small monthly fee and keep using this for my analytics. Why? I know right. Google Analytics is free so why pay for a service? I never normally pay for anything when there is a free option but in this case I think it's worth it.",[11,37554,37555],{},"I have had Google Analytics on my site for months and I never once looked at it because to be honest I am just not too keen on their platform. I am sure it's great if you are into marketing but I just find it too much and I end up getting lost. I just want to know what my most visited page was or how well a page is doing. I want things to be just easy, to be simple but also to be nice on the eye.",[23,37557,37559],{"id":37558},"easy-to-use","Easy to use",[11,37561,37562,37565,37566],{},[15,37563,37551],{"href":37549,"rel":37564},[19]," gives you a really nice way of seeing your data. A simple url. You can keep this private or make it public and share it with the world which can be helpful for sharing projects across teams. So now I can just bookmark the url and open it when I want to see what is going on. ",[121,37567],{"alt":37568,"src":37569},"plausible","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto,r_10,w_1200/v1611841528/debbie.codes/plausible_laomvr.png",[11,37571,37572],{},"I never built my site for other people. I built it as my playground, my way to explore and play with new things in Nuxt and a way that I could learn. As it is a personal project I don't always have the time to invest in it to make it more amazing and as a developer we are never fully happy with our personal site. There is always something more we could be doing, always somethings else that could be better. My blog posts were really just ways for me to remember things, my learning journal, my notes. I never wrote a blog thinking about what other people wanted to read. I probably still don't to be honest. My blog is basically my way of getting information out of my head so that I can do more things but also have a place to go back to when I need to remember the thing I got out of my head.",[23,37574,37576],{"id":37575},"why-add-analytics","Why add Analytics?",[11,37578,37579,37580,37584],{},"When you add analytics to you site you can see that people are actually reading your posts and you think, perhaps I should do more of these. If my posts help make other peoples lives easier then I really should continue to do that. Finding time to create posts is never easy of course and I do find that having the right tools really helps. I don't want to spend too long writing posts. I just want to write in Markdown, add an image and publish. I use content module for that and I wrote a ",[15,37581,37583],{"href":20940,"rel":37582},[19],"tutorial"," on it which you can read here. If you haven't already created your blog then I highly recommend that you do.",[23,37586,37588],{"id":37587},"no-more-cookie-banner","No more Cookie Banner",[11,37590,37591,37592,37595],{},"Another great thing about using ",[15,37593,37551],{"href":37549,"rel":37594},[19]," instead of Google Analytics is that your data is yours which means you are also protecting the privacy of your visitors. Your website data is never shared with advertising or marketing companies and no data is sent to third-parties and that means you don't need to have the annoying cookie banner on your site. I always hated my cookie banner which I needed to add for European laws as Google Analytics store cookies. It was such a pleasure to remove this from my site as to be honest not everyone likes cookies or being tracked so now I feel my site is just friendlier for my users. I",[23,37597,37599],{"id":37598},"great-for-performance","Great for Performance",[11,37601,37602,37603,37606,37607],{},"But by far the best thing about ",[15,37604,37551],{"href":37549,"rel":37605},[19]," is that it is lightweight meaning no more losing performance benefits by adding analytics to your site. Plausible is less than 1KB whereas Google Analytics was 17KB. Since removing Google Analytics I now have full scores on my site. ",[121,37608],{"alt":37609,"src":37610},"light house full score","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto,r_10,w_1200/v1611841869/debbie.codes/performance_we5uhd.png",[23,37612,37614],{"id":37613},"open-source","Open Source",[11,37616,37617,37618,37621],{},"And did you know that ",[15,37619,37551],{"href":37549,"rel":37620},[19]," is Open Source? It is built openly on GitHub and released under AGPL licence and can be self hosted too. I highly encourage you to give it a try, you might even like it.",[23,37623,37625],{"id":37624},"adding-plausible-to-your-nuxt-site","Adding Plausible to your Nuxt site",[11,37627,37628,37629,891],{},"With Nuxt you can add Plausible to our site by installing the ",[15,37630,37633],{"href":37631,"rel":37632},"https://github.com/moritzsternemann/vue-plausible",[19],"Plausible module",[299,37635,37637],{"className":2665,"code":37636,"language":2667,"meta":307,"style":307},"yarn add vue-plausible\n",[179,37638,37639],{"__ignoreMap":307},[1736,37640,37641,37643,37645],{"class":1738,"line":1739},[1736,37642,6575],{"class":2674},[1736,37644,2681],{"class":1935},[1736,37646,37647],{"class":1935}," vue-plausible\n",[11,37649,37650,37651,33954,37654,37657],{},"Then you need to add the ",[179,37652,37653],{},"vue-plausible",[179,37655,37656],{},"modules"," property in our Nuxt config.",[299,37659,37661],{"className":8734,"code":37660,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  modules: ['vue-plausible']\n}\n",[179,37662,37663,37671,37681],{"__ignoreMap":307},[1736,37664,37665,37667,37669],{"class":1738,"line":1739},[1736,37666,6632],{"class":4866},[1736,37668,30438],{"class":4866},[1736,37670,4914],{"class":1912},[1736,37672,37673,37676,37679],{"class":1738,"line":748},[1736,37674,37675],{"class":1912},"  modules: [",[1736,37677,37678],{"class":1935},"'vue-plausible'",[1736,37680,8420],{"class":1912},[1736,37682,37683],{"class":1738,"line":756},[1736,37684,1976],{"class":1912},[11,37686,37687,37688,37691,37692,891],{},"And finally you need to add the ",[179,37689,37690],{},"domain"," property with the value being your domain which in my case is ",[179,37693,37694],{},"debbie.codes",[299,37696,37698],{"className":8734,"code":37697,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  plausible: {\n    domain: 'ADD-YOUR-DOMAIN-HERE'\n  }\n}\n",[179,37699,37700,37708,37713,37721,37725],{"__ignoreMap":307},[1736,37701,37702,37704,37706],{"class":1738,"line":1739},[1736,37703,6632],{"class":4866},[1736,37705,30438],{"class":4866},[1736,37707,4914],{"class":1912},[1736,37709,37710],{"class":1738,"line":748},[1736,37711,37712],{"class":1912},"  plausible: {\n",[1736,37714,37715,37718],{"class":1738,"line":756},[1736,37716,37717],{"class":1912},"    domain: ",[1736,37719,37720],{"class":1935},"'ADD-YOUR-DOMAIN-HERE'\n",[1736,37722,37723],{"class":1738,"line":1755},[1736,37724,1971],{"class":1912},[1736,37726,37727],{"class":1738,"line":1761},[1736,37728,1976],{"class":1912},[23,37730,37732],{"id":37731},"plausible-dashboard","Plausible Dashboard",[11,37734,37735],{},"Of course in your Plausible dashboard you will need to add your domain and press the save button. You can then decide on your timezone, if to make your dashboard public and you can even integrate the Google Search Console.",[11,37737,37738],{},"If you really want to see your Analytics but don't have the time then you can have them emailed to you on a weekly basis or even when you get a traffic spike so you can see what is going on at the right time so you can tweet about it and share it with the world.",[2011,37740,37741],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":307,"searchDepth":748,"depth":748,"links":37743},[37744,37745,37746,37747,37748,37749,37750],{"id":37558,"depth":748,"text":37559},{"id":37575,"depth":748,"text":37576},{"id":37587,"depth":748,"text":37588},{"id":37598,"depth":748,"text":37599},{"id":37613,"depth":748,"text":37614},{"id":37624,"depth":748,"text":37625},{"id":37731,"depth":748,"text":37732},"2021-01-28","When you build your own site sometimes it's important to add analytics to see what is doing well, what your audience is spending time and perhaps find ways of improving your site","c_scale,fl_lossy,f_auto,q_auto,w_600/v1611841528/debbie.codes/plausible_laomvr",{},"/blog/nuxt-analytics",{"title":37541,"description":37752},"blog/nuxt-analytics",[5239],"J5U2Dy9AJ2YOg4iCjw-LbFVAzsVUuGea_hk69RXDcvs",{"id":37761,"title":37762,"body":37763,"canonical":788,"date":37902,"description":37903,"extension":786,"featured":787,"image":37904,"meta":37905,"navigation":790,"ogimage":788,"path":37906,"provider":5235,"published":787,"seo":37907,"stem":37908,"tags":37909,"url":788,"__hash__":37910},"blog/blog/nuxt-analyze-webpack-bundles.md","Let's Analyze your webpack bundles with Nuxt",{"type":8,"value":37764,"toc":37900},[37765,37768,37771,37821,37824,37839,37842,37858,37861,37875,37882,37888,37894,37897],[11,37766,37767],{},"It is really important to know what you are shipping to production. As we add so many third party libraries to our site sometimes we forget that perhaps they are going to have an impact on our performance or sometimes we just don't read the install instructions and install the whole library instead of only what we need. So how can we analyze our bundles in Nuxt.js?",[11,37769,37770],{},"Nuxt makes it very easy for us to dive inside our webpack bundles and take a look at what is going on. Don't worry it is not scary at all. In fact the tool is very visual and really easy to launch. You can create a command in your package.json",[299,37772,37774],{"className":5635,"code":37773,"language":5637,"meta":307,"style":307},"\"scripts\": {\n    \"dev\": \"nuxt\",\n    \"build\": \"nuxt build\",\n    \"analyze\": \"nuxt build --analyze\"\n}\n",[179,37775,37776,37783,37795,37807,37817],{"__ignoreMap":307},[1736,37777,37778,37781],{"class":1738,"line":1739},[1736,37779,37780],{"class":1935},"\"scripts\"",[1736,37782,1922],{"class":1912},[1736,37784,37785,37788,37790,37793],{"class":1738,"line":748},[1736,37786,37787],{"class":1935},"    \"dev\"",[1736,37789,3065],{"class":1912},[1736,37791,37792],{"class":1935},"\"nuxt\"",[1736,37794,1939],{"class":1912},[1736,37796,37797,37800,37802,37805],{"class":1738,"line":756},[1736,37798,37799],{"class":1935},"    \"build\"",[1736,37801,3065],{"class":1912},[1736,37803,37804],{"class":1935},"\"nuxt build\"",[1736,37806,1939],{"class":1912},[1736,37808,37809,37812,37814],{"class":1738,"line":1755},[1736,37810,37811],{"class":1935},"    \"analyze\"",[1736,37813,3065],{"class":1912},[1736,37815,37816],{"class":1935},"\"nuxt build --analyze\"\n",[1736,37818,37819],{"class":1738,"line":1761},[1736,37820,1976],{"class":1912},[11,37822,37823],{},"Or you can launch it directly in the terminal with yarn",[299,37825,37827],{"className":2665,"code":37826,"language":2667,"meta":307,"style":307},"yarn build --analyze\n",[179,37828,37829],{"__ignoreMap":307},[1736,37830,37831,37833,37836],{"class":1738,"line":1739},[1736,37832,6575],{"class":2674},[1736,37834,37835],{"class":1935}," build",[1736,37837,37838],{"class":1918}," --analyze\n",[11,37840,37841],{},"or if you don't have yarn you can use npx",[299,37843,37845],{"className":2665,"code":37844,"language":2667,"meta":307,"style":307},"npx nuxt build --analyze\n\n",[179,37846,37847],{"__ignoreMap":307},[1736,37848,37849,37851,37854,37856],{"class":1738,"line":1739},[1736,37850,2675],{"class":2674},[1736,37852,37853],{"class":1935}," nuxt",[1736,37855,37835],{"class":1935},[1736,37857,37838],{"class":1918},[11,37859,37860],{},"Personally I prefer the shorthand version which is -a",[299,37862,37864],{"className":2665,"code":37863,"language":2667,"meta":307,"style":307},"yarn build -a\n",[179,37865,37866],{"__ignoreMap":307},[1736,37867,37868,37870,37872],{"class":1738,"line":1739},[1736,37869,6575],{"class":2674},[1736,37871,37835],{"class":1935},[1736,37873,37874],{"class":1918}," -a\n",[11,37876,37877,37878],{},"Then the webpack analyzer should launch in your browser window. ",[121,37879],{"alt":37880,"src":37881},"Alt Text","https://dev-to-uploads.s3.amazonaws.com/i/zirur7w11fkojij92hn9.png",[11,37883,37884,37885],{},"The navigation bar on the left allows you to choose which chunks to show. You can see all of them or just select the ones you want. And at a quick glance you can see the size of all your chunks. ",[121,37886],{"alt":37880,"src":37887},"https://dev-to-uploads.s3.amazonaws.com/i/29oo6639ua5z4om34lp9.png",[11,37889,37890,37891],{},"You can also double click on the boxes, hover over them for more details and right click on a chunk to easily hide it or to hide all other chunks. ",[121,37892],{"alt":37880,"src":37893},"https://dev-to-uploads.s3.amazonaws.com/i/q4epyrbb8b51v2wwuxoq.png",[11,37895,37896],{},"I hope you have fun analyzing your bundles but please do not deploy bundles built with \"analyze\" mode, they are for analysis purposes only.",[2011,37898,37899],{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":37901},[],"2020-04-16","As we add so many third party libraries to our site sometimes we forget that perhaps they are going to have an impact on our performance or sometimes we just don't read the install instructions and install the whole library instead of only what we need.","photo-1434626881859-194d67b2b86f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-analyze-webpack-bundles",{"title":37762,"description":37903},"blog/nuxt-analyze-webpack-bundles",[5239,36709],"mzSLYx4GjBQJoRoL3cMHU2IfIfIngkc5ZnDN0j_VtTI",{"id":37912,"title":37913,"body":37914,"canonical":788,"date":38174,"description":37918,"extension":786,"featured":787,"image":38175,"meta":38176,"navigation":790,"ogimage":788,"path":38177,"provider":5235,"published":787,"seo":38178,"stem":38179,"tags":38180,"url":788,"__hash__":38181},"blog/blog/nuxt-configure-server-to-see-site-on-mobile.md","Configure the Nuxt.js server to see your site on your mobile",{"type":8,"value":37915,"toc":38172},[37916,37919,37922,37925,37962,37965,37970,37977,37980,37983,38019,38022,38027,38030,38051,38054,38074,38077,38103,38106,38139,38142,38156,38159,38162,38169],[11,37917,37918],{},"Sometimes you want to test out your site on your actual mobile device or tablet and not just in the dev tools. This is great when bug fixing or just to see how it looks in a real environment.",[11,37920,37921],{},"By default, the Nuxt.js development server host is localhost which is only accessible from within the host machine meaning you can't open localhost on your mobile.",[11,37923,37924],{},"However you can modify the server in your nuxt.confg.js file.",[299,37926,37928],{"className":5635,"code":37927,"language":5637,"meta":307,"style":307},"export default {\n  server: {\n    host: '0' // default: localhost\n  }\n}\n",[179,37929,37930,37938,37943,37954,37958],{"__ignoreMap":307},[1736,37931,37932,37934,37936],{"class":1738,"line":1739},[1736,37933,6632],{"class":4866},[1736,37935,30438],{"class":4866},[1736,37937,4914],{"class":1912},[1736,37939,37940],{"class":1738,"line":748},[1736,37941,37942],{"class":1912},"  server: {\n",[1736,37944,37945,37948,37951],{"class":1738,"line":756},[1736,37946,37947],{"class":1912},"    host: ",[1736,37949,37950],{"class":1935},"'0'",[1736,37952,37953],{"class":6820}," // default: localhost\n",[1736,37955,37956],{"class":1738,"line":1755},[1736,37957,1971],{"class":1912},[1736,37959,37960],{"class":1738,"line":1761},[1736,37961,1976],{"class":1912},[11,37963,37964],{},"By assigning the string value of '0' which is short for '0.0.0.0' Nuxt.js will give you your local IP address.",[11,37966,37967],{},[121,37968],{"alt":37880,"src":37969},"https://dev-to-uploads.s3.amazonaws.com/i/gve0ynnj783dtm5hbmw4.png",[11,37971,37972,37973],{},"Now instead of typing localhost to see your application you will type your local IP address. In this example mine is ",[15,37974,37975],{"href":37975,"rel":37976},"http://192.168.0.199:3000/",[19],[11,37978,37979],{},"You can now go to you mobile or tablet and open your website using that link.",[11,37981,37982],{},"The default port is 3000. Should you wish to change it you can also do so by using the port property.",[299,37984,37986],{"className":5635,"code":37985,"language":5637,"meta":307,"style":307},"export default {\n  server: {\n    port: 8000 // default: 3000\n  }\n}\n",[179,37987,37988,37996,38000,38011,38015],{"__ignoreMap":307},[1736,37989,37990,37992,37994],{"class":1738,"line":1739},[1736,37991,6632],{"class":4866},[1736,37993,30438],{"class":4866},[1736,37995,4914],{"class":1912},[1736,37997,37998],{"class":1738,"line":748},[1736,37999,37942],{"class":1912},[1736,38001,38002,38005,38008],{"class":1738,"line":756},[1736,38003,38004],{"class":1912},"    port: ",[1736,38006,38007],{"class":1918},"8000",[1736,38009,38010],{"class":6820}," // default: 3000\n",[1736,38012,38013],{"class":1738,"line":1755},[1736,38014,1971],{"class":1912},[1736,38016,38017],{"class":1738,"line":1761},[1736,38018,1976],{"class":1912},[11,38020,38021],{},"If this port is already in use, Nuxt.js will give you a random port.",[11,38023,38024],{},[121,38025],{"alt":37880,"src":38026},"https://dev-to-uploads.s3.amazonaws.com/i/46fajq67md7nvt9qaoyj.png",[11,38028,38029],{},"Although you can modify this in the nuxt.config.js file it is not advised to as it might cause you issues when hosting your site. It is much better to modify the host direct in the dev command.",[299,38031,38033],{"className":2665,"code":38032,"language":2667,"meta":307,"style":307},"HOST=0 npm run dev\n",[179,38034,38035],{"__ignoreMap":307},[1736,38036,38037,38040,38042,38044,38047,38049],{"class":1738,"line":1739},[1736,38038,38039],{"class":1912},"HOST",[1736,38041,5062],{"class":4866},[1736,38043,1290],{"class":1935},[1736,38045,38046],{"class":2674}," npm",[1736,38048,16130],{"class":1935},[1736,38050,33085],{"class":1935},[11,38052,38053],{},"or the port that you want",[299,38055,38057],{"className":2665,"code":38056,"language":2667,"meta":307,"style":307},"PORT=8000 npm run dev\n",[179,38058,38059],{"__ignoreMap":307},[1736,38060,38061,38064,38066,38068,38070,38072],{"class":1738,"line":1739},[1736,38062,38063],{"class":1912},"PORT",[1736,38065,5062],{"class":4866},[1736,38067,38007],{"class":1935},[1736,38069,38046],{"class":2674},[1736,38071,16130],{"class":1935},[1736,38073,33085],{"class":1935},[11,38075,38076],{},"or both",[299,38078,38080],{"className":2665,"code":38079,"language":2667,"meta":307,"style":307},"HOST=0 PORT=8000 npm run dev\n",[179,38081,38082],{"__ignoreMap":307},[1736,38083,38084,38086,38088,38090,38093,38095,38097,38099,38101],{"class":1738,"line":1739},[1736,38085,38039],{"class":1912},[1736,38087,5062],{"class":4866},[1736,38089,1290],{"class":1935},[1736,38091,38092],{"class":1912}," PORT",[1736,38094,5062],{"class":4866},[1736,38096,38007],{"class":1935},[1736,38098,38046],{"class":2674},[1736,38100,16130],{"class":1935},[1736,38102,33085],{"class":1935},[11,38104,38105],{},"You can even setup a script command in your package.json. For this example I will call it dev:host but you can call it whatever you like. You can add the hostname you always want to use or you can add '0' to get a random one.",[299,38107,38109],{"className":1903,"code":38108,"language":1905,"meta":307,"style":307},"{\n  \"scripts\": {\n    \"dev:host\": \"nuxt --hostname '192.168.0.199' --port 8000\"\n  }\n}\n",[179,38110,38111,38115,38121,38131,38135],{"__ignoreMap":307},[1736,38112,38113],{"class":1738,"line":1739},[1736,38114,1913],{"class":1912},[1736,38116,38117,38119],{"class":1738,"line":748},[1736,38118,17419],{"class":1918},[1736,38120,1922],{"class":1912},[1736,38122,38123,38126,38128],{"class":1738,"line":756},[1736,38124,38125],{"class":1918},"    \"dev:host\"",[1736,38127,3065],{"class":1912},[1736,38129,38130],{"class":1935},"\"nuxt --hostname '192.168.0.199' --port 8000\"\n",[1736,38132,38133],{"class":1738,"line":1755},[1736,38134,1971],{"class":1912},[1736,38136,38137],{"class":1738,"line":1761},[1736,38138,1976],{"class":1912},[11,38140,38141],{},"Which means you only have to run one command when you want to change your servers and the normal dev command for when you want to run on localhost.",[299,38143,38145],{"className":2665,"code":38144,"language":2667,"meta":307,"style":307},"npm run dev:host\n",[179,38146,38147],{"__ignoreMap":307},[1736,38148,38149,38151,38153],{"class":1738,"line":1739},[1736,38150,6565],{"class":2674},[1736,38152,16130],{"class":1935},[1736,38154,38155],{"class":1935}," dev:host\n",[11,38157,38158],{},"Although you can now see your site on your mobile you can't share this port with anyone outside of your LAN. That means if you are working remote and want to share your work in progress with someone not on the same wifi connection as you, then this method will not work.",[11,38160,38161],{},"Give it a try and start testing your mobile user experience, while in development, on an actual mobile.",[11,38163,38164],{},[15,38165,38168],{"href":38166,"rel":38167},"https://nuxtjs.org/faq/host-port",[19],"See the nuxt docs for more info",[2011,38170,38171],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":307,"searchDepth":748,"depth":748,"links":38173},[],"2020-04-21","photo-1423666639041-f56000c27a9a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjF9&auto=format&fit=crop",{},"/blog/nuxt-configure-server-to-see-site-on-mobile",{"title":37913,"description":37918},"blog/nuxt-configure-server-to-see-site-on-mobile",[5239],"DI9JcQWRCOQQMmfOmgsn4tVoczPQG5Xp5ku_P0UplRM",{"id":38183,"title":38184,"body":38185,"canonical":788,"date":38869,"description":38189,"extension":786,"featured":787,"image":38870,"meta":38871,"navigation":790,"ogimage":788,"path":38872,"provider":5235,"published":787,"seo":38873,"stem":38874,"tags":38875,"url":788,"__hash__":38876},"blog/blog/nuxt-cookie-consent.md","Adding a cookie consent banner",{"type":8,"value":38186,"toc":38867},[38187,38190,38198,38212,38215,38269,38272,38336,38339,38533,38542,38556,38559,38624,38627,38747,38750,38826,38829,38861,38864],[11,38188,38189],{},"When in Europe and using cookies we need to show a cookie consent banner so our users are aware that we are using cookies on the site.",[11,38191,38192,38193],{},"To add a cookie banner to your Nuxt.js application you can use the ",[15,38194,38197],{"href":38195,"rel":38196},"https://github.com/EvodiaAut/vue-cookieconsent-component",[19],"vue-cookieconsent-component",[299,38199,38201],{"className":2665,"code":38200,"language":2667,"meta":307,"style":307},"yarn add vue-cookieconsent-component\n",[179,38202,38203],{"__ignoreMap":307},[1736,38204,38205,38207,38209],{"class":1738,"line":1739},[1736,38206,6575],{"class":2674},[1736,38208,2681],{"class":1935},[1736,38210,38211],{"class":1935}," vue-cookieconsent-component\n",[11,38213,38214],{},"We then create a cookie box component with the props of message and link-label.",[299,38216,38219],{"className":21974,"code":38217,"filename":38218,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003CCookieConsent\n    message=\"We use Cookies for user analysis and on-page improvements!\"\n    link-label=\"Learn about cookies\"\n  />\n\u003C/template>\n","CookieBox.vue",[179,38220,38221,38229,38236,38246,38256,38261],{"__ignoreMap":307},[1736,38222,38223,38225,38227],{"class":1738,"line":1739},[1736,38224,6657],{"class":1912},[1736,38226,21985],{"class":6696},[1736,38228,6663],{"class":1912},[1736,38230,38231,38233],{"class":1738,"line":748},[1736,38232,7020],{"class":1912},[1736,38234,38235],{"class":30490},"CookieConsent\n",[1736,38237,38238,38241,38243],{"class":1738,"line":756},[1736,38239,38240],{"class":2674},"    message",[1736,38242,5062],{"class":1912},[1736,38244,38245],{"class":1935},"\"We use Cookies for user analysis and on-page improvements!\"\n",[1736,38247,38248,38251,38253],{"class":1738,"line":1755},[1736,38249,38250],{"class":2674},"    link-label",[1736,38252,5062],{"class":1912},[1736,38254,38255],{"class":1935},"\"Learn about cookies\"\n",[1736,38257,38258],{"class":1738,"line":1761},[1736,38259,38260],{"class":1912},"  />\n",[1736,38262,38263,38265,38267],{"class":1738,"line":1767},[1736,38264,8105],{"class":1912},[1736,38266,21985],{"class":6696},[1736,38268,6663],{"class":1912},[11,38270,38271],{},"And we then import our CookieConsent component.",[299,38273,38275],{"className":21974,"code":38274,"filename":38218,"language":21976,"meta":307,"style":307},"\u003Cscript>\n  import CookieConsent from 'vue-cookieconsent-component/src/components/CookieConsent.vue'\n\n  export default {\n    components: {\n      CookieConsent\n    }\n  }\n\u003C/script>\n",[179,38276,38277,38285,38298,38302,38311,38315,38320,38324,38328],{"__ignoreMap":307},[1736,38278,38279,38281,38283],{"class":1738,"line":1739},[1736,38280,6657],{"class":1912},[1736,38282,21869],{"class":6696},[1736,38284,6663],{"class":1912},[1736,38286,38287,38290,38293,38295],{"class":1738,"line":748},[1736,38288,38289],{"class":4866},"  import",[1736,38291,38292],{"class":1912}," CookieConsent ",[1736,38294,5002],{"class":4866},[1736,38296,38297],{"class":1935}," 'vue-cookieconsent-component/src/components/CookieConsent.vue'\n",[1736,38299,38300],{"class":1738,"line":756},[1736,38301,1747],{"emptyLinePlaceholder":790},[1736,38303,38304,38307,38309],{"class":1738,"line":1755},[1736,38305,38306],{"class":4866},"  export",[1736,38308,30438],{"class":4866},[1736,38310,4914],{"class":1912},[1736,38312,38313],{"class":1738,"line":1761},[1736,38314,36452],{"class":1912},[1736,38316,38317],{"class":1738,"line":1767},[1736,38318,38319],{"class":1912},"      CookieConsent\n",[1736,38321,38322],{"class":1738,"line":1772},[1736,38323,9853],{"class":1912},[1736,38325,38326],{"class":1738,"line":1778},[1736,38327,1971],{"class":1912},[1736,38329,38330,38332,38334],{"class":1738,"line":1784},[1736,38331,8105],{"class":1912},[1736,38333,21869],{"class":6696},[1736,38335,6663],{"class":1912},[11,38337,38338],{},"We then add the styles so it looks how we want it to, positioned at the top or bottom of the page.",[299,38340,38342],{"className":21974,"code":38341,"filename":38218,"language":21976,"meta":307,"style":307},"\u003Cstyle>\n  .cookie-consent {\n    display: flex;\n    padding: 10px;\n    align-items: center;\n    align-self: center;\n    justify-content: center;\n    border-bottom: 1px solid white;\n    color: white;\n  }\n  .cookie-consent button {\n    border: 1px solid white;\n    padding: 10px;\n    margin-left: 20px;\n    min-width: 140px;\n  }\n\u003C/style>\n",[179,38343,38344,38352,38359,38371,38386,38398,38409,38420,38439,38451,38455,38464,38481,38493,38507,38521,38525],{"__ignoreMap":307},[1736,38345,38346,38348,38350],{"class":1738,"line":1739},[1736,38347,6657],{"class":1912},[1736,38349,2011],{"class":6696},[1736,38351,6663],{"class":1912},[1736,38353,38354,38357],{"class":1738,"line":748},[1736,38355,38356],{"class":2674},"  .cookie-consent",[1736,38358,4914],{"class":1912},[1736,38360,38361,38364,38366,38369],{"class":1738,"line":756},[1736,38362,38363],{"class":1918},"    display",[1736,38365,3065],{"class":1912},[1736,38367,38368],{"class":1918},"flex",[1736,38370,7682],{"class":1912},[1736,38372,38373,38376,38378,38381,38384],{"class":1738,"line":1755},[1736,38374,38375],{"class":1918},"    padding",[1736,38377,3065],{"class":1912},[1736,38379,38380],{"class":1918},"10",[1736,38382,38383],{"class":4866},"px",[1736,38385,7682],{"class":1912},[1736,38387,38388,38391,38393,38396],{"class":1738,"line":1761},[1736,38389,38390],{"class":1918},"    align-items",[1736,38392,3065],{"class":1912},[1736,38394,38395],{"class":1918},"center",[1736,38397,7682],{"class":1912},[1736,38399,38400,38403,38405,38407],{"class":1738,"line":1767},[1736,38401,38402],{"class":1918},"    align-self",[1736,38404,3065],{"class":1912},[1736,38406,38395],{"class":1918},[1736,38408,7682],{"class":1912},[1736,38410,38411,38414,38416,38418],{"class":1738,"line":1772},[1736,38412,38413],{"class":1918},"    justify-content",[1736,38415,3065],{"class":1912},[1736,38417,38395],{"class":1918},[1736,38419,7682],{"class":1912},[1736,38421,38422,38425,38427,38429,38431,38434,38437],{"class":1738,"line":1778},[1736,38423,38424],{"class":1918},"    border-bottom",[1736,38426,3065],{"class":1912},[1736,38428,10249],{"class":1918},[1736,38430,38383],{"class":4866},[1736,38432,38433],{"class":1918}," solid",[1736,38435,38436],{"class":1918}," white",[1736,38438,7682],{"class":1912},[1736,38440,38441,38444,38446,38449],{"class":1738,"line":1784},[1736,38442,38443],{"class":1918},"    color",[1736,38445,3065],{"class":1912},[1736,38447,38448],{"class":1918},"white",[1736,38450,7682],{"class":1912},[1736,38452,38453],{"class":1738,"line":1790},[1736,38454,1971],{"class":1912},[1736,38456,38457,38459,38462],{"class":1738,"line":1796},[1736,38458,38356],{"class":2674},[1736,38460,38461],{"class":6696}," button",[1736,38463,4914],{"class":1912},[1736,38465,38466,38469,38471,38473,38475,38477,38479],{"class":1738,"line":2353},[1736,38467,38468],{"class":1918},"    border",[1736,38470,3065],{"class":1912},[1736,38472,10249],{"class":1918},[1736,38474,38383],{"class":4866},[1736,38476,38433],{"class":1918},[1736,38478,38436],{"class":1918},[1736,38480,7682],{"class":1912},[1736,38482,38483,38485,38487,38489,38491],{"class":1738,"line":2358},[1736,38484,38375],{"class":1918},[1736,38486,3065],{"class":1912},[1736,38488,38380],{"class":1918},[1736,38490,38383],{"class":4866},[1736,38492,7682],{"class":1912},[1736,38494,38495,38498,38500,38503,38505],{"class":1738,"line":2364},[1736,38496,38497],{"class":1918},"    margin-left",[1736,38499,3065],{"class":1912},[1736,38501,38502],{"class":1918},"20",[1736,38504,38383],{"class":4866},[1736,38506,7682],{"class":1912},[1736,38508,38509,38512,38514,38517,38519],{"class":1738,"line":2370},[1736,38510,38511],{"class":1918},"    min-width",[1736,38513,3065],{"class":1912},[1736,38515,38516],{"class":1918},"140",[1736,38518,38383],{"class":4866},[1736,38520,7682],{"class":1912},[1736,38522,38523],{"class":1738,"line":2376},[1736,38524,1971],{"class":1912},[1736,38526,38527,38529,38531],{"class":1738,"line":2381},[1736,38528,8105],{"class":1912},[1736,38530,2011],{"class":6696},[1736,38532,6663],{"class":1912},[11,38534,38535,38536,38541],{},"We can also add a package called ",[15,38537,38540],{"href":38538,"rel":38539},"https://github.com/Developmint/vue-if-bot#readme",[19],"VueIfBot"," which will hide your cookie consent for search engine crawlers.",[299,38543,38545],{"className":2665,"code":38544,"language":2667,"meta":307,"style":307},"yarn add vue-if-bot\n",[179,38546,38547],{"__ignoreMap":307},[1736,38548,38549,38551,38553],{"class":1738,"line":1739},[1736,38550,6575],{"class":2674},[1736,38552,2681],{"class":1935},[1736,38554,38555],{"class":1935}," vue-if-bot\n",[11,38557,38558],{},"We then wrap our CookieConsent component in the IfBot component.",[299,38560,38562],{"className":21974,"code":38561,"filename":38218,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003CVueIfBot>\n    \u003CCookieConsent\n      message=\"We use Cookies for user analysis and on-page improvements!\"\n      link-label=\"Learn about cookies\"\n    />\n  \u003C/VueIfBot>\n\u003C/template>\n",[179,38563,38564,38572,38580,38586,38595,38604,38608,38616],{"__ignoreMap":307},[1736,38565,38566,38568,38570],{"class":1738,"line":1739},[1736,38567,6657],{"class":1912},[1736,38569,21985],{"class":6696},[1736,38571,6663],{"class":1912},[1736,38573,38574,38576,38578],{"class":1738,"line":748},[1736,38575,7020],{"class":1912},[1736,38577,38540],{"class":30490},[1736,38579,6663],{"class":1912},[1736,38581,38582,38584],{"class":1738,"line":756},[1736,38583,6693],{"class":1912},[1736,38585,38235],{"class":30490},[1736,38587,38588,38591,38593],{"class":1738,"line":1755},[1736,38589,38590],{"class":2674},"      message",[1736,38592,5062],{"class":1912},[1736,38594,38245],{"class":1935},[1736,38596,38597,38600,38602],{"class":1738,"line":1761},[1736,38598,38599],{"class":2674},"      link-label",[1736,38601,5062],{"class":1912},[1736,38603,38255],{"class":1935},[1736,38605,38606],{"class":1738,"line":1767},[1736,38607,11973],{"class":1912},[1736,38609,38610,38612,38614],{"class":1738,"line":1772},[1736,38611,8096],{"class":1912},[1736,38613,38540],{"class":30490},[1736,38615,6663],{"class":1912},[1736,38617,38618,38620,38622],{"class":1738,"line":1778},[1736,38619,8105],{"class":1912},[1736,38621,21985],{"class":6696},[1736,38623,6663],{"class":1912},[11,38625,38626],{},"Or you could create your own content",[299,38628,38630],{"className":21974,"code":38629,"language":21976,"meta":307,"style":307},"\u003Ccookie-consent>\n  \u003Ctemplate slot=\"message\">\n    This website uses cookies\n    \u003Ca class=\"btn btn-link\" href=\"/my-cookies\">Read more\u003C/a>\n  \u003C/template>\n  \u003Ctemplate slot=\"button\">\n    \u003Cbutton class=\"btn btn-info\">Accept\u003C/button>\n  \u003C/template>\n\u003C/cookie-consent>\n",[179,38631,38632,38641,38657,38662,38688,38696,38711,38731,38739],{"__ignoreMap":307},[1736,38633,38634,38636,38639],{"class":1738,"line":1739},[1736,38635,6657],{"class":1912},[1736,38637,38638],{"class":6696},"cookie-consent",[1736,38640,6663],{"class":1912},[1736,38642,38643,38645,38647,38650,38652,38655],{"class":1738,"line":748},[1736,38644,7020],{"class":1912},[1736,38646,21985],{"class":6696},[1736,38648,38649],{"class":2674}," slot",[1736,38651,5062],{"class":1912},[1736,38653,38654],{"class":1935},"\"message\"",[1736,38656,6663],{"class":1912},[1736,38658,38659],{"class":1738,"line":756},[1736,38660,38661],{"class":1912},"    This website uses cookies\n",[1736,38663,38664,38666,38668,38670,38672,38675,38677,38679,38682,38684,38686],{"class":1738,"line":1755},[1736,38665,6693],{"class":1912},[1736,38667,15],{"class":6696},[1736,38669,36491],{"class":2674},[1736,38671,5062],{"class":1912},[1736,38673,38674],{"class":1935},"\"btn btn-link\"",[1736,38676,26966],{"class":2674},[1736,38678,5062],{"class":1912},[1736,38680,38681],{"class":1935},"\"/my-cookies\"",[1736,38683,27175],{"class":1912},[1736,38685,15],{"class":6696},[1736,38687,6663],{"class":1912},[1736,38689,38690,38692,38694],{"class":1738,"line":1761},[1736,38691,8096],{"class":1912},[1736,38693,21985],{"class":6696},[1736,38695,6663],{"class":1912},[1736,38697,38698,38700,38702,38704,38706,38709],{"class":1738,"line":1767},[1736,38699,7020],{"class":1912},[1736,38701,21985],{"class":6696},[1736,38703,38649],{"class":2674},[1736,38705,5062],{"class":1912},[1736,38707,38708],{"class":1935},"\"button\"",[1736,38710,6663],{"class":1912},[1736,38712,38713,38715,38717,38719,38721,38724,38727,38729],{"class":1738,"line":1772},[1736,38714,6693],{"class":1912},[1736,38716,14849],{"class":6696},[1736,38718,36491],{"class":2674},[1736,38720,5062],{"class":1912},[1736,38722,38723],{"class":1935},"\"btn btn-info\"",[1736,38725,38726],{"class":1912},">Accept\u003C/",[1736,38728,14849],{"class":6696},[1736,38730,6663],{"class":1912},[1736,38732,38733,38735,38737],{"class":1738,"line":1778},[1736,38734,8096],{"class":1912},[1736,38736,21985],{"class":6696},[1736,38738,6663],{"class":1912},[1736,38740,38741,38743,38745],{"class":1738,"line":1784},[1736,38742,8105],{"class":1912},[1736,38744,38638],{"class":6696},[1736,38746,6663],{"class":1912},[11,38748,38749],{},"And of course we must import it in our script tag",[299,38751,38753],{"className":21974,"code":38752,"filename":38218,"language":21976,"meta":307,"style":307},"\u003Cscript>\n  import VueIfBot from 'vue-if-bot/dist/vue-if-bot.es'\n  import CookieConsent from 'vue-cookieconsent-component/src/components/CookieConsent.vue'\n\n  export default {\n    components: {\n      VueIfBot,\n      CookieConsent\n    }\n  }\n\u003C/script>\n",[179,38754,38755,38763,38775,38785,38789,38797,38801,38806,38810,38814,38818],{"__ignoreMap":307},[1736,38756,38757,38759,38761],{"class":1738,"line":1739},[1736,38758,6657],{"class":1912},[1736,38760,21869],{"class":6696},[1736,38762,6663],{"class":1912},[1736,38764,38765,38767,38770,38772],{"class":1738,"line":748},[1736,38766,38289],{"class":4866},[1736,38768,38769],{"class":1912}," VueIfBot ",[1736,38771,5002],{"class":4866},[1736,38773,38774],{"class":1935}," 'vue-if-bot/dist/vue-if-bot.es'\n",[1736,38776,38777,38779,38781,38783],{"class":1738,"line":756},[1736,38778,38289],{"class":4866},[1736,38780,38292],{"class":1912},[1736,38782,5002],{"class":4866},[1736,38784,38297],{"class":1935},[1736,38786,38787],{"class":1738,"line":1755},[1736,38788,1747],{"emptyLinePlaceholder":790},[1736,38790,38791,38793,38795],{"class":1738,"line":1761},[1736,38792,38306],{"class":4866},[1736,38794,30438],{"class":4866},[1736,38796,4914],{"class":1912},[1736,38798,38799],{"class":1738,"line":1767},[1736,38800,36452],{"class":1912},[1736,38802,38803],{"class":1738,"line":1772},[1736,38804,38805],{"class":1912},"      VueIfBot,\n",[1736,38807,38808],{"class":1738,"line":1778},[1736,38809,38319],{"class":1912},[1736,38811,38812],{"class":1738,"line":1784},[1736,38813,9853],{"class":1912},[1736,38815,38816],{"class":1738,"line":1790},[1736,38817,1971],{"class":1912},[1736,38819,38820,38822,38824],{"class":1738,"line":1796},[1736,38821,8105],{"class":1912},[1736,38823,21869],{"class":6696},[1736,38825,6663],{"class":1912},[11,38827,38828],{},"We can then add our component to our header or footer or wherever we want to place it.",[299,38830,38833],{"className":21974,"code":38831,"filename":38832,"language":21976,"meta":307,"style":307},"\u003Cfooter>\n  \u003CTheCookieBox />\n\u003C/footer>\n","TheFooter.vue",[179,38834,38835,38844,38853],{"__ignoreMap":307},[1736,38836,38837,38839,38842],{"class":1738,"line":1739},[1736,38838,6657],{"class":1912},[1736,38840,38841],{"class":6696},"footer",[1736,38843,6663],{"class":1912},[1736,38845,38846,38848,38851],{"class":1738,"line":748},[1736,38847,7020],{"class":1912},[1736,38849,38850],{"class":30490},"TheCookieBox",[1736,38852,6739],{"class":1912},[1736,38854,38855,38857,38859],{"class":1738,"line":756},[1736,38856,8105],{"class":1912},[1736,38858,38841],{"class":6696},[1736,38860,6663],{"class":1912},[11,38862,38863],{},"And that's it. we now have a cookie consent banner on our site",[2011,38865,38866],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":38868},[],"2020-07-29","photo-1558961363-fa8fdf82db35?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-cookie-consent",{"title":38184,"description":38189},"blog/nuxt-cookie-consent",[5239],"PTkGoHH1y4A5STowwA-BWrxDnAzxsk_m-dwbmxCLnUg",{"id":38878,"title":38879,"body":38880,"canonical":788,"date":39149,"description":39150,"extension":786,"featured":787,"image":39151,"meta":39152,"navigation":790,"ogimage":788,"path":39153,"provider":3460,"published":787,"seo":39154,"stem":39155,"tags":39156,"url":788,"__hash__":39157},"blog/blog/nuxt-deploying-to-netlify.md","Deploying your Nuxt app to Netlify",{"type":8,"value":38881,"toc":39134},[38882,38885,38889,38892,38899,38903,38906,38909,38911,38914,38920,38929,38935,38939,38942,38948,38951,38957,38961,38964,38970,38974,38977,38983,38986,38992,38996,38999,39005,39009,39012,39015,39021,39025,39028,39034,39037,39041,39044,39050,39053,39056,39065,39069,39079,39104,39106,39131],[11,38883,38884],{},"When starting out in development we need to learn so many things. A new language, a new framework and new concepts. Once we have understood most of this and have some cool demo or app that we have created we just want to show it to the world or see what it would be like if it was deployed.",[23,38886,38888],{"id":38887},"where-do-we-start","Where do we start",[11,38890,38891],{},"Before it was complicated. I used to have my own hosting account and I battled many times with hosting issues and accounts and really it was not fun at all. But nowadays we have so many options and not only that, but if are deploying static sites then deploying is actually free. Cool right.",[11,38893,38894,38895],{},"With Nuxt there are many options for deployment and I am a fan of many of them to be honest but in this post I will talk about my favourite and the one I use to deploy my website on. Yes that platform is ",[15,38896,21693],{"href":38897,"rel":38898},"https://www.netlify.com/",[19],[23,38900,38902],{"id":38901},"why-netlify","Why Netlify?",[11,38904,38905],{},"For me Netlify wins on many scores but one of my favourite things about it, besides that it's free, is that it has a really easy to use interface. I like things to be simple and I like things to be nice. And did I mention it's free?",[11,38907,38908],{},"On top of that Netlify can do so much more than the simple things and you can easily add plugins for Cypress end to end testing or lighthouse tests on builds and I won't even go into serverless functions. There really is so much you can do with Netlify.",[23,38910,15435],{"id":15434},[11,38912,38913],{},"To get started with Netlify you need to create an account by signing up using your email or better still directly with your GitHub account, GitLab or BitBucket.",[11,38915,38916],{},[121,38917],{"alt":38918,"src":38919},"Sign Up to Netlify","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/netlify-sign-up_ba3ruv.png",[11,38921,38922,38923,38928],{},"Of course if you don't already have one of these then I highly recommend setting up a ",[15,38924,38927],{"href":38925,"rel":38926},"https://github.com/",[19],"GitHub account"," so you can publish your code. GitHub is great and it is also free.",[11,38930,38931],{},[121,38932],{"alt":38933,"src":38934},"GitHub","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716321/debbie.codes/blog/github_i2gvim.png",[23,38936,38938],{"id":38937},"adding-your-site","Adding your site",[11,38940,38941],{},"Once your app is in a GitHub repo you can easily add it to Netlify by pressing the button 'New Site from Git' and selecting GitHub.",[11,38943,38944],{},[121,38945],{"alt":38946,"src":38947},"new site from Netlify","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/new-site-from-netlify_fx4zmm.png",[11,38949,38950],{},"This will check you are authorized and then show you a list of your repos. From there you can choose the one you want to deploy. You will need to choose the branch. You may only want to deploy the feature or dev branch for example but generally you will want the main or master branch. Then you need to set the commands in order to build your static site. For a Nuxt static site you use the generate command and folder to be published is the dist folder.",[11,38952,38953],{},[121,38954],{"alt":38955,"src":38956},"Netlify deploy setup","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716324/debbie.codes/blog/netlify-publish-settings_o6bm32.png",[23,38958,38960],{"id":38959},"variables","Variables",[11,38962,38963],{},"If you have environment variables that you need to use then you can click the advanced button and add them in here. If you forget then don't worry. You can always add them in later.",[11,38965,38966],{},[121,38967],{"alt":38968,"src":38969},"environment variables","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/variables_xrgiwz.png",[23,38971,38973],{"id":38972},"start-deploying","Start Deploying",[11,38975,38976],{},"Once you click on 'Deploy Site' Netlify will start the deployment. In a matter of minutes your app will be deployed using a random url that is auto generated for you. While waiting for the site to deploy you can click on the link under Step 1 and check out the deploy log to see what is going on. This is a great place to look at if for some reason your deploy fails.",[11,38978,38979],{},[121,38980],{"alt":38981,"src":38982},"site deploy in progress","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/netlify-step1_zd7rhk.png",[11,38984,38985],{},"The steps are quite clear and after your site is deployed step 1 will be complete and step 2 is highlighted. You can click the link under the number 2 to Setup a custom domain. Here is where you can add your real domain that you want to use. You can then follow on to step 3 to setup HTTPS.",[11,38987,38988],{},[121,38989],{"alt":38990,"src":38991},"custom domain setup","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/netlify-step2_t3coia.png",[23,38993,38995],{"id":38994},"i-dont-have-a-domain","I don't have a domain",[11,38997,38998],{},"If you don't have a domain or perhaps for this project don't need one but don't want to have the silly url that Netlify auto generates for you then no worries. Click on the site settings button and then click on 'Change site name'. In the input box you can add anything you like and if it is available then it is yours. This domain is free and will have '.netlify.app' at the end but sometimes it is more than enough.",[11,39000,39001],{},[121,39002],{"alt":39003,"src":39004},"change the site name","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716320/debbie.codes/blog/change-site-name_noeelw.png",[23,39006,39008],{"id":39007},"deploying-a-dev-branch","Deploying a dev branch",[11,39010,39011],{},"If you want to deploy a main/master branch and a dev branch then you can still do that. Click on 'Deploys' from the main menu then choose 'Deploy Settings' and then from the menu click on 'Continuous Deployment'. Before I go on let me tell you that continuos deployment is automatically set up for you at this stage and anytime you do a push to GitHub your site will be deployed.",[11,39013,39014],{},"Now we want the same thing to happen for the dev branch too. In 'Deploy Contexts' click on 'Edit Settings'. Here you can select 'Let me add individual branches' and then choose the branch you want also to be deployed, for example 'dev'. Now anytime you push to the dev branch your app will be deployed meaning you can see it and share it with others before merging to master.",[11,39016,39017],{},[121,39018],{"alt":39019,"src":39020},"Deploy Contexts","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716321/debbie.codes/blog/deploy-contexts_gxrs7z.png",[23,39022,39024],{"id":39023},"previewing-a-previous-deploy","Previewing a Previous Deploy",[11,39026,39027],{},"At any time you can preview a previous deploy, retry a deploy and even lock publishing to a certain deploy. This is a great feature for if you break something and don't have time to fix it. Just go back to a previous deploy that was good and lock it to that one until you have time to fix and redeploy.",[11,39029,39030],{},[121,39031],{"alt":39032,"src":39033},"lock a deploy","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716319/debbie.codes/blog/lock-deploy_yc0tra.png",[11,39035,39036],{},"These are just some of the basics you can do with Netlify and there is so much more you can explore. But hopefully this post will show you how easy it is to deploy to Netlify. However if you are still not convinced there there is an even easier way by just dragging and dropping.",[23,39038,39040],{"id":39039},"drag-and-drop-option","Drag and Drop Option",[11,39042,39043],{},"Once you have generated your dist folder in VSCode using the 'generate' Command. Click on sites in Netlify and scroll to the end where you will see a box that says 'Want to deploy a new site to Netlify without connecting to Git?'. Right click the dist folder and choose 'open in finder' and then drag it and drop it into this box.",[11,39045,39046],{},[121,39047],{"alt":39048,"src":39049},"Drag and Dropt to Netlify","https://res.cloudinary.com/debsobrien/image/upload/c_scale,fl_lossy,f_auto,q_auto/v1612716319/debbie.codes/blog/netlify-drag-drop_jkk8ba.png",[11,39051,39052],{},"Your app is now deployed. This is a simple and fast way of deploying a site but if you need to make any changes you will need to locally generate it again and drag and drop it again. But still it is a cool feature.",[23,39054,19791],{"id":39055},"watch-the-video",[11,39057,39058,39059,39064],{},"If reading is not your thing then check out the video I created for the ",[15,39060,39063],{"href":39061,"rel":39062},"https://explorers.netlify.com/learn/get-started-with-nuxt/nuxt-generate-and-deploy",[19],"JamStack Explorers course"," which talks you through the whole process.",[23,39066,39068],{"id":39067},"tip","Tip",[11,39070,39071,39072,39075,39076,39078],{},"Remember to generate a static site to Netlify make sure you have the ",[179,39073,39074],{},"target","set to ",[179,39077,36276],{},"in the Nuxt config.",[299,39080,39082],{"className":8734,"code":39081,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  target: 'static'\n}\n",[179,39083,39084,39092,39100],{"__ignoreMap":307},[1736,39085,39086,39088,39090],{"class":1738,"line":1739},[1736,39087,6632],{"class":4866},[1736,39089,30438],{"class":4866},[1736,39091,4914],{"class":1912},[1736,39093,39094,39097],{"class":1738,"line":748},[1736,39095,39096],{"class":1912},"  target: ",[1736,39098,39099],{"class":1935},"'static'\n",[1736,39101,39102],{"class":1738,"line":756},[1736,39103,1976],{"class":1912},[23,39105,5706],{"id":5705},[70,39107,39108,39114,39121,39126],{},[73,39109,39110],{},[15,39111,39113],{"href":3762,"rel":39112},[19],"Jamstack Explorers course",[73,39115,39116],{},[15,39117,39120],{"href":39118,"rel":39119},"https://nuxtjs.org/docs/2.x/deployment/netlify-deployment",[19],"Nuxt Deployment Docs",[73,39122,39123],{},[15,39124,21693],{"href":38897,"rel":39125},[19],[73,39127,39128],{},[15,39129,38933],{"href":38925,"rel":39130},[19],[2011,39132,39133],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":39135},[39136,39137,39138,39139,39140,39141,39142,39143,39144,39145,39146,39147,39148],{"id":38887,"depth":748,"text":38888},{"id":38901,"depth":748,"text":38902},{"id":15434,"depth":748,"text":15435},{"id":38937,"depth":748,"text":38938},{"id":38959,"depth":748,"text":38960},{"id":38972,"depth":748,"text":38973},{"id":38994,"depth":748,"text":38995},{"id":39007,"depth":748,"text":39008},{"id":39023,"depth":748,"text":39024},{"id":39039,"depth":748,"text":39040},{"id":39055,"depth":748,"text":19791},{"id":39067,"depth":748,"text":39068},{"id":5705,"depth":748,"text":5706},"2021-02-07","If you are building a Nuxt static site and want to deploy it easily and for free then Netlify is a great choice. Let me show you how easy it is.","c_scale,fl_lossy,f_auto,q_auto,,w_600/v1612716320/debbie.codes/blog/new-site-from-netlify_fx4zmm",{},"/blog/nuxt-deploying-to-netlify",{"title":38879,"description":39150},"blog/nuxt-deploying-to-netlify",[5239],"KXsot9uH_ebdQ_wLfUOqOh27juF6vuO1bXfblYfn41k",{"id":39159,"title":39160,"body":39161,"canonical":788,"date":39444,"description":39445,"extension":786,"featured":787,"image":39446,"meta":39447,"navigation":790,"ogimage":788,"path":39448,"provider":5235,"published":787,"seo":39449,"stem":39450,"tags":39451,"url":788,"__hash__":39452},"blog/blog/nuxt-error-page.md","Creating an error page in Nuxt.js",{"type":8,"value":39162,"toc":39442},[39163,39170,39176,39226,39232,39306,39315,39358,39361,39402,39405,39432,39439],[11,39164,39165,39166,39169],{},"An error page is the page you see when you arrive at a page that can't be found. These are typically called 404 pages. To create an error page all you need to do is create an ",[179,39167,39168],{},"error.vue"," file in your layouts folder. Layouts? Yes, that is correct. Although the error page is a page, it must go inside the layouts folder.",[11,39171,39172,39173,39175],{},"In your ",[179,39174,39168],{}," page you can keep it simple by just adding a template with some text.",[299,39177,39179],{"className":21974,"code":39178,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>OOOOPPPPPPS. No page found\u003C/p>\n  \u003C/div>\n\u003C/template>\n",[179,39180,39181,39189,39197,39210,39218],{"__ignoreMap":307},[1736,39182,39183,39185,39187],{"class":1738,"line":1739},[1736,39184,6657],{"class":1912},[1736,39186,21985],{"class":6696},[1736,39188,6663],{"class":1912},[1736,39190,39191,39193,39195],{"class":1738,"line":748},[1736,39192,7020],{"class":1912},[1736,39194,6697],{"class":6696},[1736,39196,6663],{"class":1912},[1736,39198,39199,39201,39203,39206,39208],{"class":1738,"line":756},[1736,39200,6693],{"class":1912},[1736,39202,11],{"class":6696},[1736,39204,39205],{"class":1912},">OOOOPPPPPPS. No page found\u003C/",[1736,39207,11],{"class":6696},[1736,39209,6663],{"class":1912},[1736,39211,39212,39214,39216],{"class":1738,"line":1755},[1736,39213,8096],{"class":1912},[1736,39215,6697],{"class":6696},[1736,39217,6663],{"class":1912},[1736,39219,39220,39222,39224],{"class":1738,"line":1761},[1736,39221,8105],{"class":1912},[1736,39223,21985],{"class":6696},[1736,39225,6663],{"class":1912},[11,39227,39228,39229,891],{},"Or you can print a message depending on the error status which you have access to by using ",[179,39230,39231],{},"error.statusCode",[299,39233,39235],{"className":21974,"code":39234,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch1 v-if=\"error.statusCode === 404\">Page not found\u003C/h1>\n    \u003Ch1 v-else>An error occurred\u003C/h1>\n  \u003C/div>\n\u003C/template>\n",[179,39236,39237,39245,39253,39274,39290,39298],{"__ignoreMap":307},[1736,39238,39239,39241,39243],{"class":1738,"line":1739},[1736,39240,6657],{"class":1912},[1736,39242,21985],{"class":6696},[1736,39244,6663],{"class":1912},[1736,39246,39247,39249,39251],{"class":1738,"line":748},[1736,39248,7020],{"class":1912},[1736,39250,6697],{"class":6696},[1736,39252,6663],{"class":1912},[1736,39254,39255,39257,39259,39262,39264,39267,39270,39272],{"class":1738,"line":756},[1736,39256,6693],{"class":1912},[1736,39258,30550],{"class":6696},[1736,39260,39261],{"class":2674}," v-if",[1736,39263,5062],{"class":1912},[1736,39265,39266],{"class":1935},"\"error.statusCode === 404\"",[1736,39268,39269],{"class":1912},">Page not found\u003C/",[1736,39271,30550],{"class":6696},[1736,39273,6663],{"class":1912},[1736,39275,39276,39278,39280,39283,39286,39288],{"class":1738,"line":1755},[1736,39277,6693],{"class":1912},[1736,39279,30550],{"class":6696},[1736,39281,39282],{"class":2674}," v-else",[1736,39284,39285],{"class":1912},">An error occurred\u003C/",[1736,39287,30550],{"class":6696},[1736,39289,6663],{"class":1912},[1736,39291,39292,39294,39296],{"class":1738,"line":1761},[1736,39293,8096],{"class":1912},[1736,39295,6697],{"class":6696},[1736,39297,6663],{"class":1912},[1736,39299,39300,39302,39304],{"class":1738,"line":1767},[1736,39301,8105],{"class":1912},[1736,39303,21985],{"class":6696},[1736,39305,6663],{"class":1912},[11,39307,39308,39309,39312,39313,891],{},"Don't forget to add the ",[179,39310,39311],{},"error prop"," so you can use ",[179,39314,39231],{},[299,39316,39318],{"className":21974,"code":39317,"language":21976,"meta":307,"style":307},"\u003Cscript>\n  export default {\n    props: ['error']\n  }\n\u003C/script>\n",[179,39319,39320,39328,39336,39346,39350],{"__ignoreMap":307},[1736,39321,39322,39324,39326],{"class":1738,"line":1739},[1736,39323,6657],{"class":1912},[1736,39325,21869],{"class":6696},[1736,39327,6663],{"class":1912},[1736,39329,39330,39332,39334],{"class":1738,"line":748},[1736,39331,38306],{"class":4866},[1736,39333,30438],{"class":4866},[1736,39335,4914],{"class":1912},[1736,39337,39338,39341,39344],{"class":1738,"line":756},[1736,39339,39340],{"class":1912},"    props: [",[1736,39342,39343],{"class":1935},"'error'",[1736,39345,8420],{"class":1912},[1736,39347,39348],{"class":1738,"line":1755},[1736,39349,1971],{"class":1912},[1736,39351,39352,39354,39356],{"class":1738,"line":1761},[1736,39353,8105],{"class":1912},[1736,39355,21869],{"class":6696},[1736,39357,6663],{"class":1912},[11,39359,39360],{},"You can also add a layout to your error page just like you can do with any other page.",[299,39362,39364],{"className":21974,"code":39363,"language":21976,"meta":307,"style":307},"\u003Cscript>\n  export default {\n    layout: 'basic'\n  }\n\u003C/script>\n",[179,39365,39366,39374,39382,39390,39394],{"__ignoreMap":307},[1736,39367,39368,39370,39372],{"class":1738,"line":1739},[1736,39369,6657],{"class":1912},[1736,39371,21869],{"class":6696},[1736,39373,6663],{"class":1912},[1736,39375,39376,39378,39380],{"class":1738,"line":748},[1736,39377,38306],{"class":4866},[1736,39379,30438],{"class":4866},[1736,39381,4914],{"class":1912},[1736,39383,39384,39387],{"class":1738,"line":756},[1736,39385,39386],{"class":1912},"    layout: ",[1736,39388,39389],{"class":1935},"'basic'\n",[1736,39391,39392],{"class":1738,"line":1755},[1736,39393,1971],{"class":1912},[1736,39395,39396,39398,39400],{"class":1738,"line":1761},[1736,39397,8105],{"class":1912},[1736,39399,21869],{"class":6696},[1736,39401,6663],{"class":1912},[11,39403,39404],{},"So as you can see it is really easy to add an error page yet it is something we normally forget todo. Error pages can also be fun so feel free to add some nice images and don't forget to link back to the home page to make it easier for your users if they end up on your error page.",[299,39406,39408],{"className":21974,"code":39407,"language":21976,"meta":307,"style":307},"\u003CNuxtLink to=\"/\">Home page\u003C/NuxtLink>\n",[179,39409,39410],{"__ignoreMap":307},[1736,39411,39412,39414,39417,39420,39422,39425,39428,39430],{"class":1738,"line":1739},[1736,39413,6657],{"class":1912},[1736,39415,39416],{"class":30490},"NuxtLink",[1736,39418,39419],{"class":2674}," to",[1736,39421,5062],{"class":1912},[1736,39423,39424],{"class":1935},"\"/\"",[1736,39426,39427],{"class":1912},">Home page\u003C/",[1736,39429,39416],{"class":30490},[1736,39431,6663],{"class":1912},[11,39433,39434,39435,39438],{},"Note: Although this page is in the layouts folder you do not use the ",[179,39436,39437],{},"\u003CNuxt />"," component in this page.",[2011,39440,39441],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":39443},[],"2020-04-23","An error page is the page you see when you arrive at a page that can't be found. These are typically called 404 pages. To create an error page all you need to do is create an `error.vue` file in your layouts folder. Layouts?","photo-1594322436404-5a0526db4d13?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-error-page",{"title":39160,"description":39445},"blog/nuxt-error-page",[5239],"vUpu7YrYtSsrDPX3gcRKrlCnSrfGyzXoDW8YDxdi2e4",{"id":39454,"title":39455,"body":39456,"canonical":788,"date":21827,"description":39455,"extension":786,"featured":787,"image":39926,"meta":39927,"navigation":790,"ogimage":788,"path":39928,"provider":5235,"published":787,"seo":39929,"stem":39930,"tags":39931,"url":788,"__hash__":39932},"blog/blog/nuxt-fetching-graphql-queries.md","Fetching graphQL queries",{"type":8,"value":39457,"toc":39924},[39458,39461,39469,39495,39498,39528,39531,39534,39537,39540,39543,39546,39551,39643,39646,39650,39684,39687,39692,39721,39724,39727,39731,39783,39786,39789,39792,39795,39799,39876,39879,39883,39921],[11,39459,39460],{},"How to fetch data from graphQL without having to use libraries such as apollo",[11,39462,39463,39464],{},"Install ",[15,39465,39468],{"href":39466,"rel":39467},"https://http.nuxtjs.org/",[19],"http module",[299,39470,39472],{"className":2665,"code":39471,"language":2667,"meta":307,"style":307},"npm install @nuxt/http\nor\nyarn add @nuxt/http\n",[179,39473,39474,39483,39487],{"__ignoreMap":307},[1736,39475,39476,39478,39480],{"class":1738,"line":1739},[1736,39477,6565],{"class":2674},[1736,39479,4973],{"class":1935},[1736,39481,39482],{"class":1935}," @nuxt/http\n",[1736,39484,39485],{"class":1738,"line":748},[1736,39486,37337],{"class":2674},[1736,39488,39489,39491,39493],{"class":1738,"line":756},[1736,39490,6575],{"class":2674},[1736,39492,2681],{"class":1935},[1736,39494,39482],{"class":1935},[11,39496,39497],{},"Add it to the nuxt config",[299,39499,39501],{"className":4894,"code":39500,"language":4896,"meta":307,"style":307},"module.exports = {\n  modules: ['@nuxt/http']\n}\n",[179,39502,39503,39515,39524],{"__ignoreMap":307},[1736,39504,39505,39507,39509,39511,39513],{"class":1738,"line":1739},[1736,39506,4903],{"class":1918},[1736,39508,891],{"class":1912},[1736,39510,4908],{"class":1918},[1736,39512,4911],{"class":4866},[1736,39514,4914],{"class":1912},[1736,39516,39517,39519,39522],{"class":1738,"line":748},[1736,39518,37675],{"class":1912},[1736,39520,39521],{"class":1935},"'@nuxt/http'",[1736,39523,8420],{"class":1912},[1736,39525,39526],{"class":1738,"line":756},[1736,39527,1976],{"class":1912},[11,39529,39530],{},"create a plugin",[11,39532,39533],{},"pass in destructured $http and env from context",[11,39535,39536],{},"use inject method to inject our plugin so we can use it across our app",[11,39538,39539],{},"create a variable called $hasura which uses $http using the create helper and pass in the prefixUrl of our api which we can store as an env Variable",[11,39541,39542],{},"set headers for hasura admin secret if needed",[11,39544,39545],{},"inject the key of hasura with the value of $hasura using the $post method and binding our hasura variable",[11,39547,39548],{},[179,39549,39550],{},"plugins/hasura.js",[299,39552,39554],{"className":4894,"code":39553,"language":4896,"meta":307,"style":307},"export default function ({ $http, env }, inject) {\n  const $hasura = $http.create({\n    prefixUrl: env.API_HASURA_URL\n  })\n\n  inject('hasura', $hasura.$post.bind($hasura, ''))\n}\n",[179,39555,39556,39582,39599,39607,39611,39615,39639],{"__ignoreMap":307},[1736,39557,39558,39560,39562,39564,39566,39569,39571,39574,39577,39580],{"class":1738,"line":1739},[1736,39559,6632],{"class":4866},[1736,39561,30438],{"class":4866},[1736,39563,6674],{"class":4866},[1736,39565,17735],{"class":1912},[1736,39567,39568],{"class":5036},"$http",[1736,39570,829],{"class":1912},[1736,39572,39573],{"class":5036},"env",[1736,39575,39576],{"class":1912}," }, ",[1736,39578,39579],{"class":5036},"inject",[1736,39581,7246],{"class":1912},[1736,39583,39584,39586,39589,39591,39594,39597],{"class":1738,"line":748},[1736,39585,7824],{"class":4866},[1736,39587,39588],{"class":1918}," $hasura",[1736,39590,4911],{"class":4866},[1736,39592,39593],{"class":1912}," $http.",[1736,39595,39596],{"class":2674},"create",[1736,39598,5122],{"class":1912},[1736,39600,39601,39604],{"class":1738,"line":756},[1736,39602,39603],{"class":1912},"    prefixUrl: env.",[1736,39605,39606],{"class":1918},"API_HASURA_URL\n",[1736,39608,39609],{"class":1738,"line":1755},[1736,39610,13147],{"class":1912},[1736,39612,39613],{"class":1738,"line":1761},[1736,39614,1747],{"emptyLinePlaceholder":790},[1736,39616,39617,39620,39622,39625,39628,39631,39634,39636],{"class":1738,"line":1767},[1736,39618,39619],{"class":2674},"  inject",[1736,39621,7751],{"class":1912},[1736,39623,39624],{"class":1935},"'hasura'",[1736,39626,39627],{"class":1912},", $hasura.$post.",[1736,39629,39630],{"class":2674},"bind",[1736,39632,39633],{"class":1912},"($hasura, ",[1736,39635,34884],{"class":1935},[1736,39637,39638],{"class":1912},"))\n",[1736,39640,39641],{"class":1738,"line":1772},[1736,39642,1976],{"class":1912},[11,39644,39645],{},"Add your endpoint as env variable",[11,39647,39648],{},[179,39649,33252],{},[299,39651,39653],{"className":4894,"code":39652,"language":4896,"meta":307,"style":307},"export default {\n  env: {\n    API_HASURA_URL: 'https://debbie-codes.herokuapp.com/v1/graphql'\n  }\n}\n",[179,39654,39655,39663,39668,39676,39680],{"__ignoreMap":307},[1736,39656,39657,39659,39661],{"class":1738,"line":1739},[1736,39658,6632],{"class":4866},[1736,39660,30438],{"class":4866},[1736,39662,4914],{"class":1912},[1736,39664,39665],{"class":1738,"line":748},[1736,39666,39667],{"class":1912},"  env: {\n",[1736,39669,39670,39673],{"class":1738,"line":756},[1736,39671,39672],{"class":1912},"    API_HASURA_URL: ",[1736,39674,39675],{"class":1935},"'https://debbie-codes.herokuapp.com/v1/graphql'\n",[1736,39677,39678],{"class":1738,"line":1755},[1736,39679,1971],{"class":1912},[1736,39681,39682],{"class":1738,"line":1761},[1736,39683,1976],{"class":1912},[11,39685,39686],{},"import graphql and print so it prints the query",[11,39688,39689],{},[179,39690,39691],{},"pages/workshops.vue",[299,39693,39695],{"className":4894,"code":39694,"language":4896,"meta":307,"style":307},"import gql from 'graphql-tag'\nimport { print } from 'graphql/language/printer'\n",[179,39696,39697,39709],{"__ignoreMap":307},[1736,39698,39699,39701,39704,39706],{"class":1738,"line":1739},[1736,39700,4996],{"class":4866},[1736,39702,39703],{"class":1912}," gql ",[1736,39705,5002],{"class":4866},[1736,39707,39708],{"class":1935}," 'graphql-tag'\n",[1736,39710,39711,39713,39716,39718],{"class":1738,"line":748},[1736,39712,4996],{"class":4866},[1736,39714,39715],{"class":1912}," { print } ",[1736,39717,5002],{"class":4866},[1736,39719,39720],{"class":1935}," 'graphql/language/printer'\n",[11,39722,39723],{},"make your query using gpl tag",[11,39725,39726],{},"query the table and fields you want to show",[11,39728,39729],{},[179,39730,39691],{},[299,39732,39734],{"className":4894,"code":39733,"language":4896,"meta":307,"style":307},"const QUERY = gql`\n  query {\n    workshops(order_by: { date: desc }) {\n      name\n      topic\n    }\n  }\n`\n",[179,39735,39736,39751,39756,39761,39766,39771,39775,39779],{"__ignoreMap":307},[1736,39737,39738,39740,39743,39745,39748],{"class":1738,"line":1739},[1736,39739,5029],{"class":4866},[1736,39741,39742],{"class":1918}," QUERY",[1736,39744,4911],{"class":4866},[1736,39746,39747],{"class":2674}," gql",[1736,39749,39750],{"class":1935},"`\n",[1736,39752,39753],{"class":1738,"line":748},[1736,39754,39755],{"class":1935},"  query {\n",[1736,39757,39758],{"class":1738,"line":756},[1736,39759,39760],{"class":1935},"    workshops(order_by: { date: desc }) {\n",[1736,39762,39763],{"class":1738,"line":1755},[1736,39764,39765],{"class":1935},"      name\n",[1736,39767,39768],{"class":1738,"line":1761},[1736,39769,39770],{"class":1935},"      topic\n",[1736,39772,39773],{"class":1738,"line":1767},[1736,39774,9853],{"class":1935},[1736,39776,39777],{"class":1738,"line":1772},[1736,39778,1971],{"class":1935},[1736,39780,39781],{"class":1738,"line":1778},[1736,39782,39750],{"class":1935},[11,39784,39785],{},"use asyncData passing in the destructured app from context",[11,39787,39788],{},"create a variable called data which awaits the function that we created in our plugin which we have access to using app",[11,39790,39791],{},"pass in the query which will print our query",[11,39793,39794],{},"return our data",[11,39796,39797],{},[179,39798,39691],{},[299,39800,39802],{"className":4894,"code":39801,"language":4896,"meta":307,"style":307},"async asyncData({ app }) {\n    const { data } = await app.$hasura({\n      query: print(QUERY)\n    })\n    return {\n      workshops: data.workshops\n    }\n  },\n",[179,39803,39804,39815,39837,39852,39857,39863,39868,39872],{"__ignoreMap":307},[1736,39805,39806,39809,39812],{"class":1738,"line":1739},[1736,39807,39808],{"class":1912},"async ",[1736,39810,39811],{"class":2674},"asyncData",[1736,39813,39814],{"class":1912},"({ app }) {\n",[1736,39816,39817,39819,39821,39823,39825,39827,39829,39832,39835],{"class":1738,"line":748},[1736,39818,14497],{"class":4866},[1736,39820,7827],{"class":1912},[1736,39822,33342],{"class":1918},[1736,39824,7832],{"class":1912},[1736,39826,5062],{"class":4866},[1736,39828,18389],{"class":4866},[1736,39830,39831],{"class":1912}," app.",[1736,39833,39834],{"class":2674},"$hasura",[1736,39836,5122],{"class":1912},[1736,39838,39839,39842,39845,39847,39850],{"class":1738,"line":756},[1736,39840,39841],{"class":1912},"      query: ",[1736,39843,39844],{"class":2674},"print",[1736,39846,7751],{"class":1912},[1736,39848,39849],{"class":1918},"QUERY",[1736,39851,7045],{"class":1912},[1736,39853,39854],{"class":1738,"line":1755},[1736,39855,39856],{"class":1912},"    })\n",[1736,39858,39859,39861],{"class":1738,"line":1761},[1736,39860,14242],{"class":4866},[1736,39862,4914],{"class":1912},[1736,39864,39865],{"class":1738,"line":1767},[1736,39866,39867],{"class":1912},"      workshops: data.workshops\n",[1736,39869,39870],{"class":1738,"line":1772},[1736,39871,9853],{"class":1912},[1736,39873,39874],{"class":1738,"line":1778},[1736,39875,4929],{"class":1912},[11,39877,39878],{},"use our data in the template",[11,39880,39881],{},[179,39882,39691],{},[299,39884,39886],{"className":4894,"code":39885,"language":4896,"meta":307,"style":307},"\u003Cul v-for=\"(workshop, index) in workshops\" :key=\"index\">\n    \u003Cli>{{workshop.name}}: {{workshop.topic}}\u003C/li>\n\u003C/ul>\n",[179,39887,39888,39905,39916],{"__ignoreMap":307},[1736,39889,39890,39892,39894,39897,39899,39902],{"class":1738,"line":1739},[1736,39891,6657],{"class":1912},[1736,39893,70],{"class":6696},[1736,39895,39896],{"class":2674}," v-for",[1736,39898,5062],{"class":4866},[1736,39900,39901],{"class":1935},"\"(workshop, index) in workshops\"",[1736,39903,39904],{"class":30490}," :key=\"index\">\n",[1736,39906,39907,39910,39913],{"class":1738,"line":748},[1736,39908,39909],{"class":30490},"    \u003Cli>{{workshop.name}}:",[1736,39911,39912],{"class":1912}," {{workshop.topic}}",[1736,39914,39915],{"class":30490},"\u003C/li>\n",[1736,39917,39918],{"class":1738,"line":756},[1736,39919,39920],{"class":30490},"\u003C/ul>\n",[2011,39922,39923],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":39925},[],"photo-1516979187457-637abb4f9353?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop",{},"/blog/nuxt-fetching-graphql-queries",{"title":39455,"description":39455},"blog/nuxt-fetching-graphql-queries",[5239],"ybN9vIJ3CBQ37RSVGMt-6oi3AJbpSLsKmjfm-uYcp_c",{"id":39934,"title":39935,"body":39936,"canonical":788,"date":40079,"description":39940,"extension":786,"featured":787,"image":40080,"meta":40081,"navigation":790,"ogimage":788,"path":40082,"provider":5235,"published":787,"seo":40083,"stem":40084,"tags":40085,"url":788,"__hash__":40086},"blog/blog/nuxt-ignoring-files-nuxt.md","ignoring your files with Nuxt.js",{"type":8,"value":39937,"toc":40077},[39938,39941,39959,40003,40014,40023,40048,40051,40066,40074],[11,39939,39940],{},"In Nuxt.js there are 3 different ways to ignore files during the build phase. This is great for static site generation which means you can still generate your site with a broken page as it will be ignored.",[2260,39942,39943],{},[73,39944,39945,39946,39949,39950,39952,39953,10141,39955,39958],{},"Create a ",[179,39947,39948],{},".nuxtignore"," file in your root directory. You can ignore layouts, pages, store and middleware files. The ",[179,39951,39948],{}," file works the same as the ",[179,39954,2002],{},[179,39956,39957],{},".eslintignore"," in that each line is a glob pattern indicating which files should be ignored.",[299,39960,39962],{"className":5843,"code":39961,"language":786,"meta":307,"style":307},"# ignore the about page\n\npages/about.vue\n\n# ignore a page inside the blog folder\n\npages/blog/\\*.vue\n",[179,39963,39964,39970,39974,39979,39983,39988,39992],{"__ignoreMap":307},[1736,39965,39966],{"class":1738,"line":1739},[1736,39967,39969],{"class":39968},"sq-ep","# ignore the about page\n",[1736,39971,39972],{"class":1738,"line":748},[1736,39973,1747],{"emptyLinePlaceholder":790},[1736,39975,39976],{"class":1738,"line":756},[1736,39977,39978],{"class":1912},"pages/about.vue\n",[1736,39980,39981],{"class":1738,"line":1755},[1736,39982,1747],{"emptyLinePlaceholder":790},[1736,39984,39985],{"class":1738,"line":1761},[1736,39986,39987],{"class":39968},"# ignore a page inside the blog folder\n",[1736,39989,39990],{"class":1738,"line":1767},[1736,39991,1747],{"emptyLinePlaceholder":790},[1736,39993,39994,39997,40000],{"class":1738,"line":1772},[1736,39995,39996],{"class":1912},"pages/blog/",[1736,39998,39999],{"class":1918},"\\*",[1736,40001,40002],{"class":1912},".vue\n",[2260,40004,40005],{"start":748},[73,40006,40007,40008,40010,40011],{},"You can also ignore files using the ignorePrefix property by prefixing your file with a ",[179,40009,9419],{},". This is a very quick way of ignoring your file as you just need to modifying it's name. ",[179,40012,40013],{},"pages/-about.vue",[2260,40015,40016],{"start":756},[73,40017,40018,40019,40022],{},"You can also use the ignore Property in our ",[179,40020,40021],{},"nuxt.config.js file"," which is more customisable than using the ignorePrefix property. All files matching a glob pattern specified inside ignore will be ignored during the build process. This means you can easily ignore more than one file. For example to ignore all pages starting with booking:",[299,40024,40026],{"className":5635,"code":40025,"language":5637,"meta":307,"style":307},"export default {\n  ignore: 'pages/booking*.vue'\n}\n",[179,40027,40028,40036,40044],{"__ignoreMap":307},[1736,40029,40030,40032,40034],{"class":1738,"line":1739},[1736,40031,6632],{"class":4866},[1736,40033,30438],{"class":4866},[1736,40035,4914],{"class":1912},[1736,40037,40038,40041],{"class":1738,"line":748},[1736,40039,40040],{"class":1912},"  ignore: ",[1736,40042,40043],{"class":1935},"'pages/booking*.vue'\n",[1736,40045,40046],{"class":1738,"line":756},[1736,40047,1976],{"class":1912},[11,40049,40050],{},"Ignoring files is extremely helpful when developing and fixing bugs especially if you break your site and need to generate a new one. By ignoring the broken file/files/folder you can still publish your site and then work on the broken file another time without having to delete it or send it to another branch that might get forgotten about. Of course if you are ignoring files don't forget to later remove them when you want them to be added to the build phase.",[11,40052,40053,40054,40056,40057,40059,40060,40062,40063,40065],{},"All 3 options are just as good but I tend to use the ignorePrefix when I am debugging a particular page. I use the ignore property in the ",[179,40055,33252],{}," file when I need to push to production as it is easier for people to see what is going on in the pull request and clearer to understand than the prefix option. I rarely use the ",[179,40058,39948],{}," file as I find people tend to look into the ",[179,40061,33252],{}," file to see what is going on and they might not come across or understand the ",[179,40064,39948],{}," file when trying to figure out why a particular page is not building. But of course feel free to use the method that best suits you.",[11,40067,40068,40069,891],{},"For more info and examples on the ignore property see the ",[15,40070,40073],{"href":40071,"rel":40072},"https://nuxtjs.org/api/configuration-ignore",[19],"Nuxt.js docs",[2011,40075,40076],{},"html pre.shiki code .sq-ep, html code.shiki .sq-ep{--shiki-default:#005CC5;--shiki-default-font-weight:bold;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":40078},[],"2020-04-24","photo-1557487307-8dc8ec048eb8?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-ignoring-files-nuxt",{"title":39935,"description":39940},"blog/nuxt-ignoring-files-nuxt",[5239],"4ri1DYxj-fXR2ion8Y_leCTj4OHIMvCKICfdqqQepXE",{"id":40088,"title":40089,"body":40090,"canonical":788,"date":41021,"description":41022,"extension":786,"featured":787,"image":41023,"meta":41024,"navigation":790,"ogimage":788,"path":34034,"provider":3460,"published":787,"seo":41025,"stem":41026,"tags":41027,"url":788,"__hash__":41028},"blog/blog/nuxt-image.md","Improve Performance with Nuxt Image",{"type":8,"value":40091,"toc":40999},[40092,40095,40099,40107,40110,40113,40121,40129,40133,40142,40146,40149,40152,40159,40163,40172,40176,40193,40197,40206,40210,40221,40227,40231,40247,40251,40258,40294,40298,40304,40333,40337,40440,40444,40447,40486,40489,40545,40549,40585,40588,40670,40673,40684,40688,40691,40740,40744,40791,40794,40798,40805,40844,40847,40955,40959,40965,40971,40977,40983,40985,40988,40996],[11,40093,40094],{},"I built my website with Nuxt quite a while ago and have since used it as my playground for testing out features. My site contains everything you could imagine it to have and more and over time it has grown so much. It is never perfect, never done, but I am proud of how it looks and above all I am very happy with it's performance.",[23,40096,40098],{"id":40097},"running-lighthouse-tests","Running Lighthouse Tests",[11,40100,40101,40102,891],{},"Like many other people out there I open the dev tools and analyze the performance of my site. Running the lighthouse test shows me I have a full score of 100 all round. Nuxt helps you with all these performance benefits out of the box. Although I did do some extra work to make sure things were even more performant such as adding a PWA. If you are not sure how to add a PWA to your Nuxt site then check out my video, ",[15,40103,40106],{"href":40104,"rel":40105},"https://youtu.be/3RWBkPdKtBQ",[19],"Progressive Web Apps in Nuxt",[11,40108,40109],{},"Also I tip I highly recommend is to run your performance tests on a new profile window so that you don't have any interference from any chrome extensions. I have one profile called performance and use that one every time I want a more real reading.",[11,40111,40112],{},"So if my site is getting full 100% on performance then there is no more work to be done here. Right? Well this is one of the things we do wrong most of the time. We analyze the main page of the site. The home page that in my case is very simple. It only contains a few images. It is a simple page so the performance is great. But just because the home page is performant does not mean the rest of the site is. Really we should analyze the pages that have the most content, the most JavaScript and the most images.",[11,40114,40115,40116,891],{},"You can imagine how shocked I was when I ran the test on this page and got only 54 for performance. This is terrible. My very cool and performant site is actually performing terrible when there is a normal amount of content on the page. The Lighthouse test tells you where you are going wrong with links to learn more. If you are unsure of how to read LightHouse tests you can check out this free course on ",[15,40117,40120],{"href":40118,"rel":40119},"https://web.dev/lighthouse-performance/",[19],"Performance Audits",[11,40122,40123],{},[121,40124],{"src":40125,"provider":3460,"alt":40126,"loading":6375,"width":40127,"height":40128},"https://res.cloudinary.com/debsobrien/image/upload/v1631774426/debbie.codes/blog/bad-lighthouse-test_yjg64n","my site showing score of 54 and warning for images",1400,800,[23,40130,40132],{"id":40131},"running-webpagetest","Running WebPageTest",[11,40134,40135,40136,40141],{},"When it comes to testing your site Lighthouse is a great tool but sometimes you just want to understand a bit more of what is going on. For this I use ",[15,40137,40140],{"href":40138,"rel":40139},"https://www.webpagetest.org/",[19],"WebPageTest",". WebPageTest is a great tool that allows you to test your site against different browsers and devices. It also allows you to test your site against different connection types.",[138,40143,40145],{"id":40144},"analyzing-the-waterfall-view","Analyzing the Waterfall View",[11,40147,40148],{},"Once the test finishes running you can start to analyze where things are going wrong. The first thing we can look at is the waterfall view. This might seem scary to look at at first but it is actually very helpful. It shows you the different parts of the site that are taking the most time. For example the first thing you see is the first paint. This is the first time the browser is able to paint anything on the screen. This is a good indicator of how fast the site is loading.",[11,40150,40151],{},"As the name suggests it is a waterfall view and most people know what a waterfall should look like. It should go down but at a slight angle as the water falls. If you take a look at my waterfall view, well it is far from a waterfall. Line 12 to line 13 is crazy looking. The lines below it also. As you can see they are coloured purple which means they are images. What all this means is that my images or these images in specific are taking a long time to load and are affecting the performance of this page.",[11,40153,40154],{},[121,40155],{"src":40156,"provider":3460,"preset":40157,"alt":40158,"loading":6375,"width":40127,"height":40128},"https://res.cloudinary.com/debsobrien/image/upload/v1631775326/debbie.codes/blog/bad-waterfall-view_xlgw0k","blog","waterfall view of sites shows large differences and does not look like a waterfall",[138,40160,40162],{"id":40161},"largest-contentful-paint","Largest Contentful Paint",[11,40164,40165,40166,40171],{},"What a disaster. In fact if you look at the numbers the ",[15,40167,40170],{"href":40168,"rel":40169},"https://web.dev/lcp/",[19],"Largest Contentful paint"," is at 9416ms which is crazy big. A good score is anything under 2.5 seconds, needs improvement is anything from 2.5 to 4 seconds and a bad score is 4 seconds or more. Mine was more than 9 seconds. And it was just the home page of my blog. Nothing amazing just some cards with images on them.",[138,40173,40175],{"id":40174},"content-breakdown","Content Breakdown",[11,40177,40178,40181,40182,608,40187,40192],{},[15,40179,40140],{"href":40138,"rel":40180},[19]," gives you the possibility to see your content load in video format. Watching the page load can show you what is taking the most time. You can also take a look at the Content breakdown by MIME type of your site. Looking at mine this page was 90% images. This is a huge amount and no page should be 90% images unless you're selling some sort of photography on line or something. And checking the domain tab I could see my images were coming from 2 providers, ",[15,40183,40186],{"href":40184,"rel":40185},"https://cloudinary.com/",[19],"Cloudinary",[15,40188,40191],{"href":40189,"rel":40190},"https://unsplash.com/",[19],"Unsplash",". From the images tab you can run an Image Analysis of the page and see what improvements your page will have if you improve your images. Mine was showing 3.7MB for images and I could easily get this town to 235kb. That is a massive improvement.",[138,40194,40196],{"id":40195},"what-does-my-site-cost","What does my site Cost?",[11,40198,40199,40200,40205],{},"By running my site though ",[15,40201,40204],{"href":40202,"rel":40203},"https://whatdoesmysitecost.com/",[19],"What Does My Site Cost"," I could see that it was ridiculously expensive for someone to ready my blog depending on what country they were from. My content is amazing of course but this was too much. I had to improve this.",[23,40207,40209],{"id":40208},"inspecting-the-image","Inspecting the Image",[11,40211,40212,40213,40216,40217,40220],{},"So what exactly was going on with my images? I was already using ",[15,40214,40186],{"href":40184,"rel":40215},[19],". Surely they would just be performant out of the box. From the video I could see that the main image for the ",[15,40218,40170],{"href":40168,"rel":40219},[19]," was the image in the middle. Inspecting this image showed me even more. The image was rendering at a size of 480px x 256px yet the Intrinsic size, the size that an image will be displayed if no CSS is applied to change the rendering, was 4032px x 3024px and the overall size of the file was 1.5MB. This is crazy big.",[11,40222,40223],{},[121,40224],{"src":40225,"provider":3460,"preset":40157,"alt":40226,"loading":6375,"width":40127,"height":40128},"https://res.cloudinary.com/debsobrien/image/upload/v1631776449/debbie.codes/blog/inspect-image_xrjcxm","inspecting the image shows large file size of 1.5mb",[23,40228,40230],{"id":40229},"nuxt-image","Nuxt Image",[11,40232,40233,40234,40237,40238,10141,40241,40244,40245,35001],{},"This is where ",[15,40235,40230],{"href":34029,"rel":40236},[19]," comes to the rescue. Nuxt Image is a Plug-and-play image optimization for Nuxt apps. Resize and transform your images in your code using built-in optimizer or your favorite images CDN. Sounds just what I need. ",[179,40239,40240],{},"\u003CNuxtImg>",[179,40242,40243],{},"\u003Cnuxt-img>"," outputs a native img tag directly so you use it just like you would use the ",[179,40246,31400],{},[138,40248,40250],{"id":40249},"installing-nuxt-image","Installing Nuxt Image",[11,40252,40253,40254,40257],{},"The first thing you need to do is install Nuxt Image. You can do this by running ",[179,40255,40256],{},"yarn add --dev @nuxt/image"," in your project. Then add the module to buildModules section in your nuxt.config, or modules section if not using target static.",[299,40259,40261],{"className":8734,"code":40260,"language":8736,"meta":307,"style":307},"export default {\n  target: 'static',\n  buildModules: ['@nuxt/image']\n}\n",[179,40262,40263,40271,40280,40290],{"__ignoreMap":307},[1736,40264,40265,40267,40269],{"class":1738,"line":1739},[1736,40266,6632],{"class":4866},[1736,40268,30438],{"class":4866},[1736,40270,4914],{"class":1912},[1736,40272,40273,40275,40278],{"class":1738,"line":748},[1736,40274,39096],{"class":1912},[1736,40276,40277],{"class":1935},"'static'",[1736,40279,1939],{"class":1912},[1736,40281,40282,40285,40288],{"class":1738,"line":756},[1736,40283,40284],{"class":1912},"  buildModules: [",[1736,40286,40287],{"class":1935},"'@nuxt/image'",[1736,40289,8420],{"class":1912},[1736,40291,40292],{"class":1738,"line":1755},[1736,40293,1976],{"class":1912},[138,40295,40297],{"id":40296},"props-for-nuxt-image","Props for Nuxt Image",[11,40299,40300,40301,40303],{},"Then in your code you use the ",[179,40302,40240],{}," component with the various props.",[70,40305,40306,40309,40312,40315,40318,40321,40324,40327,40330],{},[73,40307,40308],{},"src: The source of the image.",[73,40310,40311],{},"alt: The alt attribute of the image.",[73,40313,40314],{},"width: The width of the image.",[73,40316,40317],{},"height: The height of the image.",[73,40319,40320],{},"loading: The loading state of the image.",[73,40322,40323],{},"provider: The provider of the image if using Cloudinary/Unsplash etc.",[73,40325,40326],{},"sizes: The sizes of the image for different screen sizes",[73,40328,40329],{},"presets: The presets you want to add to the image",[73,40331,40332],{},"class: The styles for the image.",[138,40334,40336],{"id":40335},"using-nuxt-image","Using Nuxt Image",[299,40338,40340],{"className":21974,"code":40339,"language":21976,"meta":307,"style":307},"\u003CNuxtImg\n  provider=\"cloudinary\"\n  src=\"blog/image-of-my-site\"\n  alt=\"image of my site\"\n  loading=\"lazy\"\n  preset=\"blog\"\n  width=\"640\"\n  height=\"480\"\n  sizes=\"sm:355px md:320px lg:480px\"\n  class=\"images\"\n/>\n",[179,40341,40342,40349,40358,40367,40376,40386,40396,40406,40416,40426,40436],{"__ignoreMap":307},[1736,40343,40344,40346],{"class":1738,"line":1739},[1736,40345,6657],{"class":1912},[1736,40347,40348],{"class":30490},"NuxtImg\n",[1736,40350,40351,40353,40355],{"class":1738,"line":748},[1736,40352,35968],{"class":2674},[1736,40354,5062],{"class":1912},[1736,40356,40357],{"class":1935},"\"cloudinary\"\n",[1736,40359,40360,40362,40364],{"class":1738,"line":756},[1736,40361,6836],{"class":2674},[1736,40363,5062],{"class":1912},[1736,40365,40366],{"class":1935},"\"blog/image-of-my-site\"\n",[1736,40368,40369,40371,40373],{"class":1738,"line":1755},[1736,40370,6861],{"class":2674},[1736,40372,5062],{"class":1912},[1736,40374,40375],{"class":1935},"\"image of my site\"\n",[1736,40377,40378,40381,40383],{"class":1738,"line":1761},[1736,40379,40380],{"class":2674},"  loading",[1736,40382,5062],{"class":1912},[1736,40384,40385],{"class":1935},"\"lazy\"\n",[1736,40387,40388,40391,40393],{"class":1738,"line":1767},[1736,40389,40390],{"class":2674},"  preset",[1736,40392,5062],{"class":1912},[1736,40394,40395],{"class":1935},"\"blog\"\n",[1736,40397,40398,40401,40403],{"class":1738,"line":1772},[1736,40399,40400],{"class":2674},"  width",[1736,40402,5062],{"class":1912},[1736,40404,40405],{"class":1935},"\"640\"\n",[1736,40407,40408,40411,40413],{"class":1738,"line":1778},[1736,40409,40410],{"class":2674},"  height",[1736,40412,5062],{"class":1912},[1736,40414,40415],{"class":1935},"\"480\"\n",[1736,40417,40418,40421,40423],{"class":1738,"line":1784},[1736,40419,40420],{"class":2674},"  sizes",[1736,40422,5062],{"class":1912},[1736,40424,40425],{"class":1935},"\"sm:355px md:320px lg:480px\"\n",[1736,40427,40428,40431,40433],{"class":1738,"line":1790},[1736,40429,40430],{"class":2674},"  class",[1736,40432,5062],{"class":1912},[1736,40434,40435],{"class":1935},"\"images\"\n",[1736,40437,40438],{"class":1738,"line":1796},[1736,40439,23222],{"class":1912},[138,40441,40443],{"id":40442},"setting-the-providers","Setting the Providers",[11,40445,40446],{},"The default provider for Nuxt Image is ipx or static (for target: static). Either option can be used without any configuration. Images should be stored in the static/ directory of your project. However if your images are coming from an image provider then you can set the provider to the provider of your choice.",[299,40448,40450],{"className":21974,"code":40449,"language":21976,"meta":307,"style":307},"\u003CNuxtImg\n  provider=\"cloudinary\"\n  src=\"blog/image-of-my-site\"\n  alt=\"image of my site\"\n/>\n",[179,40451,40452,40458,40466,40474,40482],{"__ignoreMap":307},[1736,40453,40454,40456],{"class":1738,"line":1739},[1736,40455,6657],{"class":1912},[1736,40457,40348],{"class":30490},[1736,40459,40460,40462,40464],{"class":1738,"line":748},[1736,40461,35968],{"class":2674},[1736,40463,5062],{"class":1912},[1736,40465,40357],{"class":1935},[1736,40467,40468,40470,40472],{"class":1738,"line":756},[1736,40469,6836],{"class":2674},[1736,40471,5062],{"class":1912},[1736,40473,40366],{"class":1935},[1736,40475,40476,40478,40480],{"class":1738,"line":1755},[1736,40477,6861],{"class":2674},[1736,40479,5062],{"class":1912},[1736,40481,40375],{"class":1935},[1736,40483,40484],{"class":1738,"line":1761},[1736,40485,23222],{"class":1912},[11,40487,40488],{},"If you have images coming from an image provider you can set it in the nuxt.config and then choose which provider it will use. Cloudinary uses the cloudinary provider whereas Unsplash uses the imgix provider.",[299,40490,40492],{"className":4894,"code":40491,"language":4896,"meta":307,"style":307},"  image: {\n    cloudinary: {\n      baseURL: 'https://res.cloudinary.com/your-account-name/image/upload/'\n    },\n    imgix: {\n      baseURL: 'https://images.unsplash.com/'\n    },\n  },\n",[179,40493,40494,40500,40507,40517,40521,40528,40537,40541],{"__ignoreMap":307},[1736,40495,40496,40498],{"class":1738,"line":1739},[1736,40497,35942],{"class":2674},[1736,40499,1922],{"class":1912},[1736,40501,40502,40505],{"class":1738,"line":748},[1736,40503,40504],{"class":2674},"    cloudinary",[1736,40506,1922],{"class":1912},[1736,40508,40509,40512,40514],{"class":1738,"line":756},[1736,40510,40511],{"class":2674},"      baseURL",[1736,40513,3065],{"class":1912},[1736,40515,40516],{"class":1935},"'https://res.cloudinary.com/your-account-name/image/upload/'\n",[1736,40518,40519],{"class":1738,"line":1755},[1736,40520,8553],{"class":1912},[1736,40522,40523,40526],{"class":1738,"line":1761},[1736,40524,40525],{"class":2674},"    imgix",[1736,40527,1922],{"class":1912},[1736,40529,40530,40532,40534],{"class":1738,"line":1767},[1736,40531,40511],{"class":2674},[1736,40533,3065],{"class":1912},[1736,40535,40536],{"class":1935},"'https://images.unsplash.com/'\n",[1736,40538,40539],{"class":1738,"line":1772},[1736,40540,8553],{"class":1912},[1736,40542,40543],{"class":1738,"line":1778},[1736,40544,4929],{"class":1912},[138,40546,40548],{"id":40547},"setting-the-presets","Setting the Presets",[299,40550,40552],{"className":21974,"code":40551,"language":21976,"meta":307,"style":307},"\u003CNuxtImg src=\"blog/image-of-my-site\" alt=\"image of my site\" preset=\"blog\" />\n",[179,40553,40554],{"__ignoreMap":307},[1736,40555,40556,40558,40561,40563,40565,40568,40570,40572,40575,40578,40580,40583],{"class":1738,"line":1739},[1736,40557,6657],{"class":1912},[1736,40559,40560],{"class":30490},"NuxtImg",[1736,40562,7026],{"class":2674},[1736,40564,5062],{"class":1912},[1736,40566,40567],{"class":1935},"\"blog/image-of-my-site\"",[1736,40569,6731],{"class":2674},[1736,40571,5062],{"class":1912},[1736,40573,40574],{"class":1935},"\"image of my site\"",[1736,40576,40577],{"class":2674}," preset",[1736,40579,5062],{"class":1912},[1736,40581,40582],{"class":1935},"\"blog\"",[1736,40584,6739],{"class":1912},[11,40586,40587],{},"Presets are a set of image modifiers that you can add to your images. By setting these as presets you don't have to add them one by one to each image and it is much easier to modify. You can have more than one preset, just give them a name and add the modifiers that should be applied to that preset.",[299,40589,40591],{"className":4894,"code":40590,"language":4896,"meta":307,"style":307},"  image: {\n    presets: {\n      blog: {\n        modifiers: {\n          format: 'webp';\n          fit: 'cover';\n          quality: '60'\n        }\n      }\n    }\n  },\n",[179,40592,40593,40599,40606,40613,40620,40632,40644,40654,40658,40662,40666],{"__ignoreMap":307},[1736,40594,40595,40597],{"class":1738,"line":1739},[1736,40596,35942],{"class":2674},[1736,40598,1922],{"class":1912},[1736,40600,40601,40604],{"class":1738,"line":748},[1736,40602,40603],{"class":2674},"    presets",[1736,40605,1922],{"class":1912},[1736,40607,40608,40611],{"class":1738,"line":756},[1736,40609,40610],{"class":2674},"      blog",[1736,40612,1922],{"class":1912},[1736,40614,40615,40618],{"class":1738,"line":1755},[1736,40616,40617],{"class":2674},"        modifiers",[1736,40619,1922],{"class":1912},[1736,40621,40622,40625,40627,40630],{"class":1738,"line":1761},[1736,40623,40624],{"class":2674},"          format",[1736,40626,3065],{"class":1912},[1736,40628,40629],{"class":1935},"'webp'",[1736,40631,7682],{"class":1912},[1736,40633,40634,40637,40639,40642],{"class":1738,"line":1767},[1736,40635,40636],{"class":2674},"          fit",[1736,40638,3065],{"class":1912},[1736,40640,40641],{"class":1935},"'cover'",[1736,40643,7682],{"class":1912},[1736,40645,40646,40649,40651],{"class":1738,"line":1772},[1736,40647,40648],{"class":2674},"          quality",[1736,40650,3065],{"class":1912},[1736,40652,40653],{"class":1935},"'60'\n",[1736,40655,40656],{"class":1738,"line":1778},[1736,40657,26121],{"class":1912},[1736,40659,40660],{"class":1738,"line":1784},[1736,40661,14448],{"class":1912},[1736,40663,40664],{"class":1738,"line":1790},[1736,40665,9853],{"class":1912},[1736,40667,40668],{"class":1738,"line":1796},[1736,40669,4929],{"class":1912},[11,40671,40672],{},"The presets we are using here are:",[70,40674,40675,40678,40681],{},[73,40676,40677],{},"format: WebP, serves the image as a WebP image when browsers support it.",[73,40679,40680],{},"fit: Cover, Preserving aspect ratio, ensure the image covers both provided dimensions by cropping/clipping to fit.",[73,40682,40683],{},"quality: 60, the quality of the image.",[138,40685,40687],{"id":40686},"extra-modifiers","Extra Modifiers",[11,40689,40690],{},"As well as the standard modifiers you can also use the the modifiers from the provider. For example if using Cloudinary we can use the roundCorner modifier to add a rounded corner to the image.",[299,40692,40694],{"className":21974,"code":40693,"language":21976,"meta":307,"style":307},"\u003CNuxtImg\n  provider=\"cloudinary\"\n  src=\"blog/image-of-my-site\"\n  alt=\"image of my site\"\n  :modifiers=\"{ roundCorner: '0:100' }\"\n/>\n",[179,40695,40696,40702,40710,40718,40726,40736],{"__ignoreMap":307},[1736,40697,40698,40700],{"class":1738,"line":1739},[1736,40699,6657],{"class":1912},[1736,40701,40348],{"class":30490},[1736,40703,40704,40706,40708],{"class":1738,"line":748},[1736,40705,35968],{"class":2674},[1736,40707,5062],{"class":1912},[1736,40709,40357],{"class":1935},[1736,40711,40712,40714,40716],{"class":1738,"line":756},[1736,40713,6836],{"class":2674},[1736,40715,5062],{"class":1912},[1736,40717,40366],{"class":1935},[1736,40719,40720,40722,40724],{"class":1738,"line":1755},[1736,40721,6861],{"class":2674},[1736,40723,5062],{"class":1912},[1736,40725,40375],{"class":1935},[1736,40727,40728,40731,40733],{"class":1738,"line":1761},[1736,40729,40730],{"class":2674},"  :modifiers",[1736,40732,5062],{"class":1912},[1736,40734,40735],{"class":1935},"\"{ roundCorner: '0:100' }\"\n",[1736,40737,40738],{"class":1738,"line":1767},[1736,40739,23222],{"class":1912},[138,40741,40743],{"id":40742},"setting-the-width-and-height","Setting the Width and Height",[299,40745,40747],{"className":21974,"code":40746,"language":21976,"meta":307,"style":307},"\u003CNuxtImg\n  src=\"blog/image-of-my-site\"\n  alt=\"image of my site\"\n  width=\"640\"\n  height=\"480\"\n/>\n",[179,40748,40749,40755,40763,40771,40779,40787],{"__ignoreMap":307},[1736,40750,40751,40753],{"class":1738,"line":1739},[1736,40752,6657],{"class":1912},[1736,40754,40348],{"class":30490},[1736,40756,40757,40759,40761],{"class":1738,"line":748},[1736,40758,6836],{"class":2674},[1736,40760,5062],{"class":1912},[1736,40762,40366],{"class":1935},[1736,40764,40765,40767,40769],{"class":1738,"line":756},[1736,40766,6861],{"class":2674},[1736,40768,5062],{"class":1912},[1736,40770,40375],{"class":1935},[1736,40772,40773,40775,40777],{"class":1738,"line":1755},[1736,40774,40400],{"class":2674},[1736,40776,5062],{"class":1912},[1736,40778,40405],{"class":1935},[1736,40780,40781,40783,40785],{"class":1738,"line":1761},[1736,40782,40410],{"class":2674},[1736,40784,5062],{"class":1912},[1736,40786,40415],{"class":1935},[1736,40788,40789],{"class":1738,"line":1767},[1736,40790,23222],{"class":1912},[11,40792,40793],{},"Setting the image's width and height helps the browser allocate the correct space for the image which ensures that content below the image doesn't shift once the image is loaded.",[138,40795,40797],{"id":40796},"setting-the-sizes","Setting the sizes",[11,40799,40800,40801,40804],{},"This will create a ",[179,40802,40803],{},"srcset"," attribute for the image adding each of the image sizes.",[299,40806,40808],{"className":21974,"code":40807,"language":21976,"meta":307,"style":307},"\u003CNuxtImg\n  src=\"blog/image-of-my-site\"\n  alt=\"image of my site\"\n  sizes=\"sm:355px md:320px lg:480px\"\n/>\n",[179,40809,40810,40816,40824,40832,40840],{"__ignoreMap":307},[1736,40811,40812,40814],{"class":1738,"line":1739},[1736,40813,6657],{"class":1912},[1736,40815,40348],{"class":30490},[1736,40817,40818,40820,40822],{"class":1738,"line":748},[1736,40819,6836],{"class":2674},[1736,40821,5062],{"class":1912},[1736,40823,40366],{"class":1935},[1736,40825,40826,40828,40830],{"class":1738,"line":756},[1736,40827,6861],{"class":2674},[1736,40829,5062],{"class":1912},[1736,40831,40375],{"class":1935},[1736,40833,40834,40836,40838],{"class":1738,"line":1755},[1736,40835,40420],{"class":2674},[1736,40837,5062],{"class":1912},[1736,40839,40425],{"class":1935},[1736,40841,40842],{"class":1738,"line":1761},[1736,40843,23222],{"class":1912},[11,40845,40846],{},"You can define the size of the image to be displayed at different screen sizes. The default screen sizes predefined by Nuxt Image are:",[299,40848,40850],{"className":4894,"code":40849,"language":4896,"meta":307,"style":307},"image: {\n    screens: {\n      xs: 320,\n      sm: 640,\n      md: 768,\n      lg: 1024,\n      xl: 1280,\n      xxl: 1536,\n      '2xl': 1536\n    },\n  }\n",[179,40851,40852,40858,40865,40877,40889,40901,40913,40925,40937,40947,40951],{"__ignoreMap":307},[1736,40853,40854,40856],{"class":1738,"line":1739},[1736,40855,23384],{"class":2674},[1736,40857,1922],{"class":1912},[1736,40859,40860,40863],{"class":1738,"line":748},[1736,40861,40862],{"class":2674},"    screens",[1736,40864,1922],{"class":1912},[1736,40866,40867,40870,40872,40875],{"class":1738,"line":756},[1736,40868,40869],{"class":2674},"      xs",[1736,40871,3065],{"class":1912},[1736,40873,40874],{"class":1918},"320",[1736,40876,1939],{"class":1912},[1736,40878,40879,40882,40884,40887],{"class":1738,"line":1755},[1736,40880,40881],{"class":2674},"      sm",[1736,40883,3065],{"class":1912},[1736,40885,40886],{"class":1918},"640",[1736,40888,1939],{"class":1912},[1736,40890,40891,40894,40896,40899],{"class":1738,"line":1761},[1736,40892,40893],{"class":2674},"      md",[1736,40895,3065],{"class":1912},[1736,40897,40898],{"class":1918},"768",[1736,40900,1939],{"class":1912},[1736,40902,40903,40906,40908,40911],{"class":1738,"line":1767},[1736,40904,40905],{"class":2674},"      lg",[1736,40907,3065],{"class":1912},[1736,40909,40910],{"class":1918},"1024",[1736,40912,1939],{"class":1912},[1736,40914,40915,40918,40920,40923],{"class":1738,"line":1772},[1736,40916,40917],{"class":2674},"      xl",[1736,40919,3065],{"class":1912},[1736,40921,40922],{"class":1918},"1280",[1736,40924,1939],{"class":1912},[1736,40926,40927,40930,40932,40935],{"class":1738,"line":1778},[1736,40928,40929],{"class":2674},"      xxl",[1736,40931,3065],{"class":1912},[1736,40933,40934],{"class":1918},"1536",[1736,40936,1939],{"class":1912},[1736,40938,40939,40942,40944],{"class":1738,"line":1784},[1736,40940,40941],{"class":1935},"      '2xl'",[1736,40943,3065],{"class":1912},[1736,40945,40946],{"class":1918},"1536\n",[1736,40948,40949],{"class":1738,"line":1790},[1736,40950,8553],{"class":1912},[1736,40952,40953],{"class":1738,"line":1796},[1736,40954,1971],{"class":1912},[23,40956,40958],{"id":40957},"running-the-tests-against-nuxt-image","Running the Tests against Nuxt Image",[11,40960,40961,40962,40964],{},"Using Nuxt Image instead of the regular ",[121,40963],{}," tag and adding some basic presets such as quality and format we can see the difference not only by inspecting the code and seeing the file sizes reduced dramatically but also in the waterfall view. Now this is what a waterfall should look like.",[11,40966,40967],{},[121,40968],{"src":40969,"provider":3460,"preset":40157,"alt":40970,"loading":6375,"width":40127,"height":40128},"https://res.cloudinary.com/debsobrien/image/upload/v1631790377/debbie.codes/blog/good-waterfall-view_cynn46","good waterfall view with images greatly reduced",[11,40972,40973,40974,891],{},"Not just is our waterfall view better but our images now only make up 30% of the page, down from 90%. This is a huge improvement. The load time as also been reduced and to a total of 1167ms meaning we are very much in green. Our page now costs a lot less to run and our Lighthouse tests are showing 100%. All this thanks to using ",[15,40975,40230],{"href":34029,"rel":40976},[19],[11,40978,40979],{},[121,40980],{"src":40981,"provider":3460,"preset":40157,"alt":40982,"loading":6375,"width":40127,"height":40128},"https://res.cloudinary.com/debsobrien/image/upload/v1631791512/debbie.codes/blog/good-lighthouse-test_joltel","lighthouse test results showing full 100 scores",[23,40984,3294],{"id":3293},[11,40986,40987],{},"It really is time to start moving your images to use Nuxt Image. It will improve your performance tremendously and will make your site much more accessible to everyone. Don't forget to run tests on the pages of your site that are most image heavy and run tests regularly as things can change over time.",[11,40989,40990,40991,891],{},"If you would like to take a look at the test results for this example you can see it ",[15,40992,40995],{"href":40993,"rel":40994},"https://www.webpagetest.org/result/210916_BiDc62_d5383efaf92f31414b0aee197e02062c/1/details/",[19],"here",[2011,40997,40998],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":41000},[41001,41002,41008,41009,41019,41020],{"id":40097,"depth":748,"text":40098},{"id":40131,"depth":748,"text":40132,"children":41003},[41004,41005,41006,41007],{"id":40144,"depth":756,"text":40145},{"id":40161,"depth":756,"text":40162},{"id":40174,"depth":756,"text":40175},{"id":40195,"depth":756,"text":40196},{"id":40208,"depth":748,"text":40209},{"id":40229,"depth":748,"text":40230,"children":41010},[41011,41012,41013,41014,41015,41016,41017,41018],{"id":40249,"depth":756,"text":40250},{"id":40296,"depth":756,"text":40297},{"id":40335,"depth":756,"text":40336},{"id":40442,"depth":756,"text":40443},{"id":40547,"depth":756,"text":40548},{"id":40686,"depth":756,"text":40687},{"id":40742,"depth":756,"text":40743},{"id":40796,"depth":756,"text":40797},{"id":40957,"depth":748,"text":40958},{"id":3293,"depth":748,"text":3294},"2021-09-16","Showing the benefits of Nuxt image by analyzing the performance of my website before and after adding the Nuxt Image module.","v1631791512/debbie.codes/blog/good-lighthouse-test_joltel",{"loading":3458},{"title":40089,"description":41022},"blog/nuxt-image",[5239,36709],"POF5GsbVc9rcwE0OrI1DA4tHdp2m3OXsbKlcYBMGqbw",{"id":41030,"title":41031,"body":41032,"canonical":788,"date":41376,"description":41377,"extension":786,"featured":787,"image":41378,"meta":41379,"navigation":790,"ogimage":788,"path":34579,"provider":3460,"published":787,"seo":41380,"stem":41381,"tags":41382,"url":788,"__hash__":41383},"blog/blog/nuxt-lite-youtube-embeds.md","Lite YouTube Embeds",{"type":8,"value":41033,"toc":41364},[41034,41037,41041,41044,41050,41053,41059,41063,41070,41074,41077,41081,41084,41087,41091,41098,41102,41105,41111,41115,41118,41124,41128,41136,41139,41144,41157,41162,41189,41201,41213,41218,41245,41250,41296,41300,41311,41318,41325,41328,41332,41361],[11,41035,41036],{},"We normally like to add YouTube embeds to our site so that users can easily click on a video and just watch it right there in your site and it is up to them if they want to watch in full screen or watch direct in YouTube itself. But when you have a page with lots of YouTube videos it can negatively affect performance. I have such a page where I load all the interviews and as I have done quite a lot of them the page takes a few seconds to fully load those videos.",[23,41038,41040],{"id":41039},"lighthouse-tests-with-iframe","Lighthouse Tests with iframe",[11,41042,41043],{},"For those of you who regularly run a lighthouse test you might have come across this warning:",[11,41045,41046],{},[121,41047],{"alt":41048,"src":41049},"lighthouse test facade warning","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1611938884/debbie.codes/blog/Screenshot_2021-01-29_at_10.37.43_meeyhu.png",[11,41051,41052],{},"Also you will see when testing on mobile how this page which has many YouTube embeds was giving a poor performance score with the Largest Contentful Paint of 8.8 seconds and time to interactive of 6.3 seconds.",[11,41054,41055],{},[121,41056],{"alt":41057,"src":41058},"lighthouse test scores","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1611939169/debbie.codes/blog/Screenshot_2021-01-29_at_16.34.14_jcv8ok.png",[23,41060,41062],{"id":41061},"performance-tab-with-iframe","Performance Tab with iframe",[11,41064,41065,41066],{},"And if we investigate a little further we can see in the Performance tab how long it is taking for the video frame to load meaning our user is getting this black empty box for quite a while until the video is ready. ",[121,41067],{"alt":41068,"src":41069},"Chrome performance test","https://res.cloudinary.com/debsobrien/image/upload/c_scale,f_auto,q_auto,w_1400/v1611938884/debbie.codes/blog/Screenshot_2021-01-29_at_16.36.35_fa339w.png",[23,41071,41073],{"id":41072},"solution","Solution",[11,41075,41076],{},"Did you know that third-party embeds can be lazily loaded on interaction? A facade is used in place of the third-party content until the user actually interacts with it.",[23,41078,41080],{"id":41079},"whats-a-facade","What's a facade?",[11,41082,41083],{},"A facade is a static element which looks like the actual embedded third-party but it is not functional which makes it much quicker to load. In this case it's basically just an image that looks like a YouTube video.",[11,41085,41086],{},"Instead of adding the third-party embed direct in our HTML, we load the page with a static element that looks similar to it and once the user interacts with this facade by placing their mouse over it, it then preconnects to the third-party resource and when the user clicks it replaces itself with the third-party product, in this case the actual video.",[23,41088,41090],{"id":41089},"lighthouse-tests-with-a-facade","Lighthouse Tests with a facade",[11,41092,41093,41094],{},"Running a Lighthouse test using a facade we can see a huge difference especially in our Largest Contentful Paint which is in orange instead of red and with 3.8 seconds versus 8.8 seconds from the previous test. Also the time to Interactive has dropped from 6.3s to 3.3s and is now in green. This is a massive increase in performance with very little work needed. ",[121,41095],{"alt":41096,"src":41097},"Lighthouse test scores","https://res.cloudinary.com/debsobrien/image/upload/c_scale,f_auto,q_auto,w_1400/v1611939855/debbie.codes/blog/Screenshot_2021-01-29_at_18.03.19_tlqdpj.png",[23,41099,41101],{"id":41100},"performance-tab-with-a-facade","Performance Tab with a facade",[11,41103,41104],{},"And if we check in our performance tab in the Chrome dev tools we can see how our image for our video has loaded so much earlier in the timeline and the total blocking time is also reduced from 1366ms to 567ms.",[11,41106,41107],{},[121,41108],{"alt":41109,"src":41110},"Chrome performance tab","https://res.cloudinary.com/debsobrien/image/upload/c_scale,f_auto,q_auto,w_1400/v1611939855/debbie.codes/blog/Screenshot_2021-01-29_at_18.03.52_yuqsr3.png",[23,41112,41114],{"id":41113},"watch-the-difference","Watch the difference",[11,41116,41117],{},"In order to really see the difference in load times you just have to watch this video which shows the YouTube videos loaded with an iframe first followed by the YouTube videos loaded with a facade. The difference really is insane.",[11,41119,41120],{},[121,41121],{"alt":41122,"src":41123},"Difference between iframe and facade","https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1611941119/debbie.codes/blog/facade_upiufl.gif",[23,41125,41127],{"id":41126},"lite-youtube-embed-library","Lite YouTube Embed library",[11,41129,41130,41131,41135],{},"In order to add a facade to your YouTube videos I recommend the ",[15,41132,41127],{"href":41133,"rel":41134},"https://www.npmjs.com/package/lite-youtube-embed",[19]," by Paul Irish.",[11,41137,41138],{},"The library instructions are pretty easy to follow but If working with Nuxt then here's what you can do:",[2260,41140,41141],{},[73,41142,41143],{},"Install the package",[299,41145,41147],{"className":2665,"code":41146,"language":2667,"meta":307,"style":307},"yarn add lite-youtube-embed\n",[179,41148,41149],{"__ignoreMap":307},[1736,41150,41151,41153,41155],{"class":1738,"line":1739},[1736,41152,6575],{"class":2674},[1736,41154,2681],{"class":1935},[1736,41156,34597],{"class":1935},[2260,41158,41159],{"start":748},[73,41160,41161],{},"Add the CSS. You can add it to the component you are using directly or to the global styles in the CSS array in the Nuxt config file.",[299,41163,41165],{"className":8734,"code":41164,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  css: ['node_modules/lite-youtube-embed/src/lite-yt-embed.css']\n}\n",[179,41166,41167,41175,41185],{"__ignoreMap":307},[1736,41168,41169,41171,41173],{"class":1738,"line":1739},[1736,41170,6632],{"class":4866},[1736,41172,30438],{"class":4866},[1736,41174,4914],{"class":1912},[1736,41176,41177,41180,41183],{"class":1738,"line":748},[1736,41178,41179],{"class":1912},"  css: [",[1736,41181,41182],{"class":1935},"'node_modules/lite-youtube-embed/src/lite-yt-embed.css'",[1736,41184,8420],{"class":1912},[1736,41186,41187],{"class":1738,"line":756},[1736,41188,1976],{"class":1912},[2260,41190,41191],{"start":756},[73,41192,41193,41194,41196,41197,41200],{},"Create a plugin that imports the JavaScript for the package. Make sure to add ",[179,41195,37234],{}," to the end of the filename so that it is only loaded client side as this library makes use of the ",[179,41198,41199],{},"HTMLElement"," which we only have access to in the browser.",[299,41202,41205],{"className":8734,"code":41203,"filename":41204,"language":8736,"meta":307,"style":307},"import 'lite-youtube-embed'\n","plugins/youtube.client.js",[179,41206,41207],{"__ignoreMap":307},[1736,41208,41209,41211],{"class":1738,"line":1739},[1736,41210,4996],{"class":4866},[1736,41212,34612],{"class":1935},[2260,41214,41215],{"start":1755},[73,41216,41217],{},"Register the plugin in your Nuxt config file so it can be used across the site.",[299,41219,41221],{"className":8734,"code":41220,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  plugins: ['~/plugins/youtube.client.js']\n}\n",[179,41222,41223,41231,41241],{"__ignoreMap":307},[1736,41224,41225,41227,41229],{"class":1738,"line":1739},[1736,41226,6632],{"class":4866},[1736,41228,30438],{"class":4866},[1736,41230,4914],{"class":1912},[1736,41232,41233,41236,41239],{"class":1738,"line":748},[1736,41234,41235],{"class":1912},"  plugins: [",[1736,41237,41238],{"class":1935},"'~/plugins/youtube.client.js'",[1736,41240,8420],{"class":1912},[1736,41242,41243],{"class":1738,"line":756},[1736,41244,1976],{"class":1912},[2260,41246,41247],{"start":1761},[73,41248,41249],{},"Use the element where ever you want and just change the videoid for your video and the playlabel for whatever you want it to be. You can also add params for start times etc",[299,41251,41254],{"className":8734,"code":41252,"filename":41253,"language":8736,"meta":307,"style":307},"\u003Clite-youtube\n  videoid=\"ogfYd705cRs\"\n  playlabel=\"Play: Keynote (Google I/O '18)\"\n  params=\"controls=0&start=10&end=30&modestbranding=2&rel=0&enablejsapi=1\n/>\n","components/vidoes.vue",[179,41255,41256,41263,41273,41283,41292],{"__ignoreMap":307},[1736,41257,41258,41260],{"class":1738,"line":1739},[1736,41259,6657],{"class":1912},[1736,41261,41262],{"class":1918},"lite-youtube\n",[1736,41264,41265,41268,41270],{"class":1738,"line":748},[1736,41266,41267],{"class":2674},"  videoid",[1736,41269,5062],{"class":4866},[1736,41271,41272],{"class":1935},"\"ogfYd705cRs\"\n",[1736,41274,41275,41278,41280],{"class":1738,"line":756},[1736,41276,41277],{"class":2674},"  playlabel",[1736,41279,5062],{"class":4866},[1736,41281,41282],{"class":1935},"\"Play: Keynote (Google I/O '18)\"\n",[1736,41284,41285,41287,41289],{"class":1738,"line":1755},[1736,41286,34227],{"class":2674},[1736,41288,5062],{"class":4866},[1736,41290,41291],{"class":1935},"\"controls=0&start=10&end=30&modestbranding=2&rel=0&enablejsapi=1\n",[1736,41293,41294],{"class":1738,"line":1761},[1736,41295,23222],{"class":1935},[23,41297,41299],{"id":41298},"behind-the-scenes","Behind the Scenes",[11,41301,41302,41303,41306,41307],{},"Your video will now appear on your page and if you inspect the ",[179,41304,41305],{},"HTML"," you will see the following: ",[121,41308],{"alt":41309,"src":41310},"rendered html from library","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1611942696/debbie.codes/blog/Screenshot_2021-01-29_at_18.50.42_bdctou.png",[11,41312,41313,41314],{},"And as you can see a background image was added, which looks the same as the video, without you even having to create it. This is how a facade works. ",[121,41315],{"alt":41316,"src":41317},"background image","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1611942698/debbie.codes/blog/Screenshot_2021-01-29_at_18.51.18_ifwfgr.png",[11,41319,41320,41321],{},"As soon as the user clicks on the video the famous iframe is loaded and the video plays straight away. The user has no idea you used a facade. All they know is that it loaded super fast and therefore they had a very good user experience. ",[121,41322],{"alt":41323,"src":41324},"html showing iframe","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1611943517/debbie.codes/blog/Screenshot_2021-01-29_at_19.04.47_bhero2.png",[11,41326,41327],{},"I encourage you to give it a try or at least investigate more about it in the links below.",[23,41329,41331],{"id":41330},"recommended-links","Recommended Links",[70,41333,41334,41341,41348,41354],{},[73,41335,41336],{},[15,41337,41340],{"href":41338,"rel":41339},"https://debbie.codes/resources/interviews",[19],"Page using a facade on this site",[73,41342,41343],{},[15,41344,41347],{"href":41345,"rel":41346},"https://web.dev/third-party-facades/",[19],"Third Party Facades from Web.dev",[73,41349,41350],{},[15,41351,41353],{"href":41133,"rel":41352},[19],"Lite YouTube Embed by Paul Irish",[73,41355,41356],{},[15,41357,41360],{"href":41358,"rel":41359},"https://paulirish.github.io/lite-youtube-embed/",[19],"Demo from Paula Irish",[2011,41362,41363],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":307,"searchDepth":748,"depth":748,"links":41365},[41366,41367,41368,41369,41370,41371,41372,41373,41374,41375],{"id":41039,"depth":748,"text":41040},{"id":41061,"depth":748,"text":41062},{"id":41072,"depth":748,"text":41073},{"id":41079,"depth":748,"text":41080},{"id":41089,"depth":748,"text":41090},{"id":41100,"depth":748,"text":41101},{"id":41113,"depth":748,"text":41114},{"id":41126,"depth":748,"text":41127},{"id":41298,"depth":748,"text":41299},{"id":41330,"depth":748,"text":41331},"2021-01-29","If you are adding YouTube videos to your site you might notice that they can load quite slowly especially when loading iframes. However this is a better way. With this library your YouTube videos will load super fast and your site will be more performant because of it.","f_auto,q_auto/v1611939855/debbie.codes/blog/Screenshot_2021-01-29_at_18.03.52_yuqsr3",{},{"title":41031,"description":41377},"blog/nuxt-lite-youtube-embeds",[5239,36709],"5XDF_ImM2bwB8RLspmxVTWh_Tt9O0z8GpFQvh1UhArY",{"id":41385,"title":41386,"body":41387,"canonical":788,"date":42286,"description":42287,"extension":786,"featured":787,"image":42288,"meta":42289,"navigation":790,"ogimage":788,"path":42290,"provider":5235,"published":787,"seo":42291,"stem":42292,"tags":42293,"url":788,"__hash__":42294},"blog/blog/nuxt-loading.md","Customizing the Nuxt Loading Component",{"type":8,"value":41388,"toc":42277},[41389,41392,41396,41399,41408,41432,41435,41456,41517,41524,41528,41534,41577,41581,41586,41610,41614,41617,41623,41665,41669,41672,41730,41756,41861,41864,41894,41897,42245,42248,42250,42274],[11,41390,41391],{},"We can customize the Loading for our Client Side applications as well as the loading Progress bar for our server rendered applications and we can even customize this to create our own. Of course if your site is super fast you might need even need to worry about loading bars 🙊.",[23,41393,41395],{"id":41394},"client-side-loading","Client Side Loading",[11,41397,41398],{},"When running Nuxt on client side, there is no content from the server side on the first page load. So, instead of showing a blank page while the page loads, Nuxt gives you a spinner which you can customize to add your own colours or background and even change the indicator.",[11,41400,41401,41402,41405,41406,891],{},"With Nuxt the default spinner works out of the box when ",[179,41403,41404],{},"ssr"," is set to ",[179,41407,33509],{},[299,41409,41411],{"className":8734,"code":41410,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  ssr: false\n}\n",[179,41412,41413,41421,41428],{"__ignoreMap":307},[1736,41414,41415,41417,41419],{"class":1738,"line":1739},[1736,41416,6632],{"class":4866},[1736,41418,30438],{"class":4866},[1736,41420,4914],{"class":1912},[1736,41422,41423,41426],{"class":1738,"line":748},[1736,41424,41425],{"class":1912},"  ssr: ",[1736,41427,35177],{"class":1918},[1736,41429,41430],{"class":1738,"line":756},[1736,41431,1976],{"class":1912},[11,41433,41434],{},"If you don't see the spinner then just slow down your internet connection using the network tab in the dev tools.",[11,41436,41437,41438,41441,41442,41445,41446,41451,41452,41455],{},"We can also customize this default spinner. Using the ",[179,41439,41440],{},"loadingIndicator"," property we can change the colour and the background and we can also change the default ",[179,41443,41444],{},"circle"," indicator. Nuxt gives us ",[15,41447,41450],{"href":41448,"rel":41449},"https://tobiasahlin.com/spinkit/",[19],"built in indicators"," and we don't have to do anything to use them except change the name for the indicator we want to use. Let's use the ",[179,41453,41454],{},"rotating-plane"," indicator.",[299,41457,41459],{"className":8734,"code":41458,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  ssr: false,\n  loadingIndicator: {\n    name: 'rotating-plane',\n    color: 'blue',\n    background: 'red'\n  }\n}\n",[179,41460,41461,41469,41477,41482,41491,41501,41509,41513],{"__ignoreMap":307},[1736,41462,41463,41465,41467],{"class":1738,"line":1739},[1736,41464,6632],{"class":4866},[1736,41466,30438],{"class":4866},[1736,41468,4914],{"class":1912},[1736,41470,41471,41473,41475],{"class":1738,"line":748},[1736,41472,41425],{"class":1912},[1736,41474,33509],{"class":1918},[1736,41476,1939],{"class":1912},[1736,41478,41479],{"class":1738,"line":756},[1736,41480,41481],{"class":1912},"  loadingIndicator: {\n",[1736,41483,41484,41486,41489],{"class":1738,"line":1755},[1736,41485,31037],{"class":1912},[1736,41487,41488],{"class":1935},"'rotating-plane'",[1736,41490,1939],{"class":1912},[1736,41492,41493,41496,41499],{"class":1738,"line":1761},[1736,41494,41495],{"class":1912},"    color: ",[1736,41497,41498],{"class":1935},"'blue'",[1736,41500,1939],{"class":1912},[1736,41502,41503,41506],{"class":1738,"line":1767},[1736,41504,41505],{"class":1912},"    background: ",[1736,41507,41508],{"class":1935},"'red'\n",[1736,41510,41511],{"class":1738,"line":1772},[1736,41512,1971],{"class":1912},[1736,41514,41515],{"class":1738,"line":1778},[1736,41516,1976],{"class":1912},[11,41518,41519,41520,891],{},"There are many more examples for you to play around with so check out the ",[15,41521,41523],{"href":41448,"rel":41522},[19],"docs",[23,41525,41527],{"id":41526},"loading-bar-for-ssr","Loading Bar for SSR",[11,41529,41530,41531,41533],{},"Nuxt gives you a loading progress bar that's shown between routes. We can customize the colour, size, duration and direction of the progress bar and much more. This can be done in the nuxt config file using the ",[179,41532,23423],{}," property.",[299,41535,41537],{"className":8734,"code":41536,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  loading: {\n    color: 'DodgerBlue',\n    height: '10px'\n  }\n}\n",[179,41538,41539,41547,41552,41561,41569,41573],{"__ignoreMap":307},[1736,41540,41541,41543,41545],{"class":1738,"line":1739},[1736,41542,6632],{"class":4866},[1736,41544,30438],{"class":4866},[1736,41546,4914],{"class":1912},[1736,41548,41549],{"class":1738,"line":748},[1736,41550,41551],{"class":1912},"  loading: {\n",[1736,41553,41554,41556,41559],{"class":1738,"line":756},[1736,41555,41495],{"class":1912},[1736,41557,41558],{"class":1935},"'DodgerBlue'",[1736,41560,1939],{"class":1912},[1736,41562,41563,41566],{"class":1738,"line":1755},[1736,41564,41565],{"class":1912},"    height: ",[1736,41567,41568],{"class":1935},"'10px'\n",[1736,41570,41571],{"class":1738,"line":1761},[1736,41572,1971],{"class":1912},[1736,41574,41575],{"class":1738,"line":1767},[1736,41576,1976],{"class":1912},[138,41578,41580],{"id":41579},"disabling-the-loading-bar","Disabling the Loading Bar",[11,41582,41583,41584,891],{},"Should you want to disable this you can set the value to ",[179,41585,33509],{},[299,41587,41589],{"className":8734,"code":41588,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  loading: false\n}\n",[179,41590,41591,41599,41606],{"__ignoreMap":307},[1736,41592,41593,41595,41597],{"class":1738,"line":1739},[1736,41594,6632],{"class":4866},[1736,41596,30438],{"class":4866},[1736,41598,4914],{"class":1912},[1736,41600,41601,41604],{"class":1738,"line":748},[1736,41602,41603],{"class":1912},"  loading: ",[1736,41605,35177],{"class":1918},[1736,41607,41608],{"class":1738,"line":756},[1736,41609,1976],{"class":1912},[138,41611,41613],{"id":41612},"customizing-the-loading-bar","Customizing the Loading Bar",[11,41615,41616],{},"You can modify the duration of the loader to be more or less than the default, which is set to 5 seconds. Generally you don't want the loader to keep going when your page has already loaded. However it is not possible for the loading component to know in advance how long loading a page will take and therefore not possible to accurately animate the progress bar to 100% of the loading time.",[11,41618,41619,41620,41622],{},"However we can change the default behavior by setting continuous to true in the ",[179,41621,23423],{}," property so it will keep animating when loading takes longer than expected. As soon as the progress bar reaches 100% which we have set to be 3 seconds it will then start shrinking back to 0% again for another 3 seconds and then it will start again until the page finishes loading.",[299,41624,41626],{"className":8734,"code":41625,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  loading: {\n    duration: 3000,\n    continuous: true\n  }\n}\n",[179,41627,41628,41636,41640,41650,41657,41661],{"__ignoreMap":307},[1736,41629,41630,41632,41634],{"class":1738,"line":1739},[1736,41631,6632],{"class":4866},[1736,41633,30438],{"class":4866},[1736,41635,4914],{"class":1912},[1736,41637,41638],{"class":1738,"line":748},[1736,41639,41551],{"class":1912},[1736,41641,41642,41645,41648],{"class":1738,"line":756},[1736,41643,41644],{"class":1912},"    duration: ",[1736,41646,41647],{"class":1918},"3000",[1736,41649,1939],{"class":1912},[1736,41651,41652,41655],{"class":1738,"line":1755},[1736,41653,41654],{"class":1912},"    continuous: ",[1736,41656,21380],{"class":1918},[1736,41658,41659],{"class":1738,"line":1761},[1736,41660,1971],{"class":1912},[1736,41662,41663],{"class":1738,"line":1767},[1736,41664,1976],{"class":1912},[138,41666,41668],{"id":41667},"creating-a-custom-loading-component","Creating a Custom Loading Component",[11,41670,41671],{},"However we can improve this further by creating a custom loading component.",[299,41673,41676],{"className":8734,"code":41674,"filename":41675,"language":8736,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv v-if=\"loading\">\n    \u003Cp>Loading...\u003C/p>\n  \u003C/div>\n\u003C/template>\n","components/LoadingBar.vue",[179,41677,41678,41686,41701,41714,41722],{"__ignoreMap":307},[1736,41679,41680,41682,41684],{"class":1738,"line":1739},[1736,41681,6657],{"class":1912},[1736,41683,21985],{"class":6696},[1736,41685,6663],{"class":1912},[1736,41687,41688,41690,41692,41694,41696,41699],{"class":1738,"line":748},[1736,41689,7020],{"class":1912},[1736,41691,6697],{"class":6696},[1736,41693,39261],{"class":2674},[1736,41695,5062],{"class":4866},[1736,41697,41698],{"class":1935},"\"loading\"",[1736,41700,6663],{"class":1912},[1736,41702,41703,41705,41707,41710,41712],{"class":1738,"line":756},[1736,41704,6693],{"class":1912},[1736,41706,11],{"class":6696},[1736,41708,41709],{"class":1912},">Loading...\u003C/",[1736,41711,11],{"class":6696},[1736,41713,6663],{"class":1912},[1736,41715,41716,41718,41720],{"class":1738,"line":1755},[1736,41717,8096],{"class":1912},[1736,41719,6697],{"class":6696},[1736,41721,6663],{"class":1912},[1736,41723,41724,41726,41728],{"class":1738,"line":1761},[1736,41725,8105],{"class":1912},[1736,41727,21985],{"class":6696},[1736,41729,6663],{"class":1912},[11,41731,41732,41733,41736,41737,3733,41739,41741,41742,41736,41745,3733,41747,41749,41750,608,41753,891],{},"In our data property we can set loading to false and add a method with a ",[179,41734,41735],{},"start()"," method which sets ",[179,41738,23423],{},[179,41740,27129],{}," and a ",[179,41743,41744],{},"finish()",[179,41746,23423],{},[179,41748,33509],{},". These 2 methods are required in order for the loader to work. We also have other optional methods ",[179,41751,41752],{},"fail(error)",[179,41754,41755],{},"increase(num)",[299,41757,41759],{"className":8734,"code":41758,"filename":41675,"language":8736,"meta":307,"style":307},"\u003Cscript>\nexport default {\n  data: () => ({\n    loading: false\n  }),\n  methods: {\n    start() {\n      this.loading = true\n    },\n    finish() {\n      this.loading = false\n    }\n  }\n}\n\u003C/script>\n",[179,41760,41761,41769,41773,41783,41790,41795,41799,41806,41819,41823,41830,41841,41845,41849,41853],{"__ignoreMap":307},[1736,41762,41763,41765,41767],{"class":1738,"line":1739},[1736,41764,6657],{"class":1912},[1736,41766,21869],{"class":6696},[1736,41768,6663],{"class":1912},[1736,41770,41771],{"class":1738,"line":748},[1736,41772,21876],{"class":1912},[1736,41774,41775,41778,41780],{"class":1738,"line":756},[1736,41776,41777],{"class":1912},"  data: () ",[1736,41779,7013],{"class":4866},[1736,41781,41782],{"class":1912}," ({\n",[1736,41784,41785,41788],{"class":1738,"line":1755},[1736,41786,41787],{"class":1912},"    loading: ",[1736,41789,35177],{"class":1918},[1736,41791,41792],{"class":1738,"line":1761},[1736,41793,41794],{"class":1912},"  }),\n",[1736,41796,41797],{"class":1738,"line":1767},[1736,41798,21881],{"class":1912},[1736,41800,41801,41804],{"class":1738,"line":1772},[1736,41802,41803],{"class":2674},"    start",[1736,41805,6680],{"class":1912},[1736,41807,41808,41811,41814,41816],{"class":1738,"line":1778},[1736,41809,41810],{"class":1918},"      this",[1736,41812,41813],{"class":1912},".loading ",[1736,41815,5062],{"class":4866},[1736,41817,41818],{"class":1918}," true\n",[1736,41820,41821],{"class":1738,"line":1784},[1736,41822,8553],{"class":1912},[1736,41824,41825,41828],{"class":1738,"line":1790},[1736,41826,41827],{"class":2674},"    finish",[1736,41829,6680],{"class":1912},[1736,41831,41832,41834,41836,41838],{"class":1738,"line":1796},[1736,41833,41810],{"class":1918},[1736,41835,41813],{"class":1912},[1736,41837,5062],{"class":4866},[1736,41839,41840],{"class":1918}," false\n",[1736,41842,41843],{"class":1738,"line":2353},[1736,41844,9853],{"class":1912},[1736,41846,41847],{"class":1738,"line":2358},[1736,41848,1971],{"class":1912},[1736,41850,41851],{"class":1738,"line":2364},[1736,41852,1976],{"class":1912},[1736,41854,41855,41857,41859],{"class":1738,"line":2370},[1736,41856,8105],{"class":1912},[1736,41858,21869],{"class":6696},[1736,41860,6663],{"class":1912},[11,41862,41863],{},"We now need to update our nuxt config to tell it to use our component instead of the default loader by adding our component as the value of the loading property.",[299,41865,41867],{"className":8734,"code":41866,"filename":33252,"language":8736,"meta":307,"style":307},"export default {\n  loading: '@/components/LoadingBar.vue',\n...\n}\n",[179,41868,41869,41877,41886,41890],{"__ignoreMap":307},[1736,41870,41871,41873,41875],{"class":1738,"line":1739},[1736,41872,6632],{"class":4866},[1736,41874,30438],{"class":4866},[1736,41876,4914],{"class":1912},[1736,41878,41879,41881,41884],{"class":1738,"line":748},[1736,41880,41603],{"class":1912},[1736,41882,41883],{"class":1935},"'@/components/LoadingBar.vue'",[1736,41885,1939],{"class":1912},[1736,41887,41888],{"class":1738,"line":756},[1736,41889,5073],{"class":4866},[1736,41891,41892],{"class":1738,"line":1755},[1736,41893,1976],{"class":1912},[11,41895,41896],{},"And we now have some loading text appearing on our page when the page needs time to load. Of course this can be improved further by adding some styles and a spinner using CSS instead of the loading text.",[299,41898,41900],{"className":8734,"code":41899,"filename":41675,"language":8736,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv v-if=\"loading\" class=\"loading-page\">\n    \u003Cdiv class=\"loading\">\u003C/div>\n  \u003C/div>\n\u003C/template>\n\n\u003Cstyle scoped>\n.loading-page {\n  position: fixed;\n  top: 0;\n  right: 0;\n  z-index: 1000;\n  padding: 1rem;\n  text-align: center;\n  font-size: 3rem;\n  font-family: sans-serif;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n}\n.loading {\n  display: inline-block;\n  width: 1.5rem;\n  height: 1.5rem;\n  border: 4px solid rgba(9, 133, 81, 0.705);\n  border-radius: 50%;\n  border-top-color: #158876;\n  animation: spin 1s ease-in-out infinite;\n}\n@keyframes spin {\n  to {\n    -webkit-transform: rotate(360deg);\n  }\n}\n\u003C/style>\n",[179,41901,41902,41910,41931,41949,41957,41965,41969,41980,41985,41990,41999,42008,42023,42028,42038,42048,42062,42067,42077,42087,42097,42101,42106,42116,42121,42126,42156,42174,42193,42204,42208,42213,42218,42229,42233,42237],{"__ignoreMap":307},[1736,41903,41904,41906,41908],{"class":1738,"line":1739},[1736,41905,6657],{"class":1912},[1736,41907,21985],{"class":6696},[1736,41909,6663],{"class":1912},[1736,41911,41912,41914,41916,41918,41920,41922,41924,41926,41929],{"class":1738,"line":748},[1736,41913,7020],{"class":1912},[1736,41915,6697],{"class":6696},[1736,41917,39261],{"class":2674},[1736,41919,5062],{"class":4866},[1736,41921,41698],{"class":1935},[1736,41923,36491],{"class":2674},[1736,41925,5062],{"class":4866},[1736,41927,41928],{"class":1935},"\"loading-page\"",[1736,41930,6663],{"class":1912},[1736,41932,41933,41935,41937,41939,41941,41943,41945,41947],{"class":1738,"line":756},[1736,41934,6693],{"class":1912},[1736,41936,6697],{"class":6696},[1736,41938,36491],{"class":2674},[1736,41940,5062],{"class":4866},[1736,41942,41698],{"class":1935},[1736,41944,8252],{"class":1912},[1736,41946,6697],{"class":6696},[1736,41948,6663],{"class":1912},[1736,41950,41951,41953,41955],{"class":1738,"line":1755},[1736,41952,8096],{"class":1912},[1736,41954,6697],{"class":6696},[1736,41956,6663],{"class":1912},[1736,41958,41959,41961,41963],{"class":1738,"line":1761},[1736,41960,8105],{"class":1912},[1736,41962,21985],{"class":6696},[1736,41964,6663],{"class":1912},[1736,41966,41967],{"class":1738,"line":1767},[1736,41968,1747],{"emptyLinePlaceholder":790},[1736,41970,41971,41973,41975,41978],{"class":1738,"line":1772},[1736,41972,6657],{"class":1912},[1736,41974,2011],{"class":6696},[1736,41976,41977],{"class":2674}," scoped",[1736,41979,6663],{"class":1912},[1736,41981,41982],{"class":1738,"line":1778},[1736,41983,41984],{"class":1912},".loading-page {\n",[1736,41986,41987],{"class":1738,"line":1784},[1736,41988,41989],{"class":1912},"  position: fixed;\n",[1736,41991,41992,41995,41997],{"class":1738,"line":1790},[1736,41993,41994],{"class":1912},"  top: ",[1736,41996,1290],{"class":1918},[1736,41998,7682],{"class":1912},[1736,42000,42001,42004,42006],{"class":1738,"line":1796},[1736,42002,42003],{"class":1912},"  right: ",[1736,42005,1290],{"class":1918},[1736,42007,7682],{"class":1912},[1736,42009,42010,42013,42015,42018,42021],{"class":1738,"line":2353},[1736,42011,42012],{"class":1912},"  z",[1736,42014,9419],{"class":4866},[1736,42016,42017],{"class":1912},"index: ",[1736,42019,42020],{"class":1918},"1000",[1736,42022,7682],{"class":1912},[1736,42024,42025],{"class":1738,"line":2358},[1736,42026,42027],{"class":1912},"  padding: 1rem;\n",[1736,42029,42030,42033,42035],{"class":1738,"line":2364},[1736,42031,42032],{"class":1912},"  text",[1736,42034,9419],{"class":4866},[1736,42036,42037],{"class":1912},"align: center;\n",[1736,42039,42040,42043,42045],{"class":1738,"line":2370},[1736,42041,42042],{"class":1912},"  font",[1736,42044,9419],{"class":4866},[1736,42046,42047],{"class":1912},"size: 3rem;\n",[1736,42049,42050,42052,42054,42057,42059],{"class":1738,"line":2376},[1736,42051,42042],{"class":1912},[1736,42053,9419],{"class":4866},[1736,42055,42056],{"class":1912},"family: sans",[1736,42058,9419],{"class":4866},[1736,42060,42061],{"class":1912},"serif;\n",[1736,42063,42064],{"class":1738,"line":2381},[1736,42065,42066],{"class":1912},"  display: flex;\n",[1736,42068,42069,42072,42074],{"class":1738,"line":2387},[1736,42070,42071],{"class":1912},"  flex",[1736,42073,9419],{"class":4866},[1736,42075,42076],{"class":1912},"direction: column;\n",[1736,42078,42079,42082,42084],{"class":1738,"line":2393},[1736,42080,42081],{"class":1912},"  align",[1736,42083,9419],{"class":4866},[1736,42085,42086],{"class":1912},"items: center;\n",[1736,42088,42089,42092,42094],{"class":1738,"line":2398},[1736,42090,42091],{"class":1912},"  justify",[1736,42093,9419],{"class":4866},[1736,42095,42096],{"class":1912},"content: center;\n",[1736,42098,42099],{"class":1738,"line":2404},[1736,42100,1976],{"class":1912},[1736,42102,42103],{"class":1738,"line":6959},[1736,42104,42105],{"class":1912},".loading {\n",[1736,42107,42108,42111,42113],{"class":1738,"line":7296},[1736,42109,42110],{"class":1912},"  display: inline",[1736,42112,9419],{"class":4866},[1736,42114,42115],{"class":1912},"block;\n",[1736,42117,42118],{"class":1738,"line":7305},[1736,42119,42120],{"class":1912},"  width: 1.5rem;\n",[1736,42122,42123],{"class":1738,"line":7310},[1736,42124,42125],{"class":1912},"  height: 1.5rem;\n",[1736,42127,42128,42131,42134,42136,42139,42141,42144,42146,42149,42151,42154],{"class":1738,"line":9659},[1736,42129,42130],{"class":1912},"  border: 4px solid ",[1736,42132,42133],{"class":2674},"rgba",[1736,42135,7751],{"class":1912},[1736,42137,42138],{"class":1918},"9",[1736,42140,829],{"class":1912},[1736,42142,42143],{"class":1918},"133",[1736,42145,829],{"class":1912},[1736,42147,42148],{"class":1918},"81",[1736,42150,829],{"class":1912},[1736,42152,42153],{"class":1918},"0.705",[1736,42155,16106],{"class":1912},[1736,42157,42158,42161,42163,42166,42169,42172],{"class":1738,"line":9680},[1736,42159,42160],{"class":1912},"  border",[1736,42162,9419],{"class":4866},[1736,42164,42165],{"class":1912},"radius: ",[1736,42167,42168],{"class":1918},"50",[1736,42170,42171],{"class":4866},"%",[1736,42173,7682],{"class":1912},[1736,42175,42176,42178,42180,42183,42185,42188,42191],{"class":1738,"line":9699},[1736,42177,42160],{"class":1912},[1736,42179,9419],{"class":4866},[1736,42181,42182],{"class":1912},"top",[1736,42184,9419],{"class":4866},[1736,42186,42187],{"class":1912},"color: #",[1736,42189,42190],{"class":1918},"158876",[1736,42192,7682],{"class":1912},[1736,42194,42195,42198,42201],{"class":1738,"line":9704},[1736,42196,42197],{"class":1912},"  animation: spin 1s ease",[1736,42199,42200],{"class":4866},"-in-",[1736,42202,42203],{"class":1912},"out infinite;\n",[1736,42205,42206],{"class":1738,"line":9715},[1736,42207,1976],{"class":1912},[1736,42209,42210],{"class":1738,"line":9727},[1736,42211,42212],{"class":1912},"@keyframes spin {\n",[1736,42214,42215],{"class":1738,"line":9739},[1736,42216,42217],{"class":1912},"  to {\n",[1736,42219,42220,42223,42226],{"class":1738,"line":9750},[1736,42221,42222],{"class":1912},"    -webkit-transform: ",[1736,42224,42225],{"class":2674},"rotate",[1736,42227,42228],{"class":1912},"(360deg);\n",[1736,42230,42231],{"class":1738,"line":9761},[1736,42232,1971],{"class":1912},[1736,42234,42235],{"class":1738,"line":9767},[1736,42236,1976],{"class":1912},[1736,42238,42239,42241,42243],{"class":1738,"line":9778},[1736,42240,8105],{"class":1912},[1736,42242,2011],{"class":6696},[1736,42244,6663],{"class":1912},[11,42246,42247],{},"And that's it. I hope you have fun creating custom loaders with really cool animations.",[23,42249,5706],{"id":5705},[70,42251,42252,42260,42267],{},[73,42253,42254,42255],{},"Nuxt Docs on ",[15,42256,42259],{"href":42257,"rel":42258},"https://nuxtjs.org/docs/2.x/features/loading",[19],"Loading",[73,42261,42262,42263],{},"Built in Indicators from ",[15,42264,42266],{"href":41448,"rel":42265},[19],"Spin Kit",[73,42268,42269,42270],{},"Custom Loading Component ",[15,42271,22290],{"href":42272,"rel":42273},"https://nuxtjs.org/examples/custom-loading-component",[19],[2011,42275,42276],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":307,"searchDepth":748,"depth":748,"links":42278},[42279,42280,42285],{"id":41394,"depth":748,"text":41395},{"id":41526,"depth":748,"text":41527,"children":42281},[42282,42283,42284],{"id":41579,"depth":756,"text":41580},{"id":41612,"depth":756,"text":41613},{"id":41667,"depth":756,"text":41668},{"id":5705,"depth":748,"text":5706},"2021-02-16","We can customize the Loading for our Client Side applications as well as the loading Progress bar for our server rendered applications and we can even customize this to create our own.","photo-1607434472257-d9f8e57a643d?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&",{"loading":3458},"/blog/nuxt-loading",{"title":41386,"description":42287},"blog/nuxt-loading",[5239],"ilxhcFgK-eE1ViZ9vjPpiObaDZIa7bevJgoDF939hzY",{"id":42296,"title":42297,"body":42298,"canonical":788,"date":42857,"description":42302,"extension":786,"featured":787,"image":42858,"meta":42859,"navigation":790,"ogimage":788,"path":42860,"provider":5235,"published":787,"seo":42861,"stem":42862,"tags":42863,"url":788,"__hash__":42864},"blog/blog/nuxt-loading-progress-bar.md","The Nuxt Loading Progress Bar",{"type":8,"value":42299,"toc":42855},[42300,42303,42306,42338,42341,42397,42400,42422,42425,42497,42500,42822,42825,42849,42852],[11,42301,42302],{},"Nuxt.js automatically gives you a loading progress bar component which is shown between routes. Did you know you can customise it, disable it or create your own component?",[11,42304,42305],{},"To change the colour for example all you have to do is add the colour you want to the loading property in your nuxt.config.js file.",[299,42307,42309],{"className":5635,"code":42308,"language":5637,"meta":307,"style":307},"export default {\n  loading: {\n    color: 'blue'\n  }\n}\n",[179,42310,42311,42319,42323,42330,42334],{"__ignoreMap":307},[1736,42312,42313,42315,42317],{"class":1738,"line":1739},[1736,42314,6632],{"class":4866},[1736,42316,30438],{"class":4866},[1736,42318,4914],{"class":1912},[1736,42320,42321],{"class":1738,"line":748},[1736,42322,41551],{"class":1912},[1736,42324,42325,42327],{"class":1738,"line":756},[1736,42326,41495],{"class":1912},[1736,42328,42329],{"class":1935},"'blue'\n",[1736,42331,42332],{"class":1738,"line":1755},[1736,42333,1971],{"class":1912},[1736,42335,42336],{"class":1738,"line":1761},[1736,42337,1976],{"class":1912},[11,42339,42340],{},"There are many other things you can change such as the height, the duration, the direction for rtl sites and if it should keep animating the progress bar when loading takes longer than the duration.",[299,42342,42344],{"className":5635,"code":42343,"language":5637,"meta":307,"style":307},"export default {\n  loading: {\n    height: '10px',\n    duration: 1000,\n    rtl: true,\n    continuous: true\n  }\n}\n",[179,42345,42346,42354,42358,42366,42374,42383,42389,42393],{"__ignoreMap":307},[1736,42347,42348,42350,42352],{"class":1738,"line":1739},[1736,42349,6632],{"class":4866},[1736,42351,30438],{"class":4866},[1736,42353,4914],{"class":1912},[1736,42355,42356],{"class":1738,"line":748},[1736,42357,41551],{"class":1912},[1736,42359,42360,42362,42364],{"class":1738,"line":756},[1736,42361,41565],{"class":1912},[1736,42363,30417],{"class":1935},[1736,42365,1939],{"class":1912},[1736,42367,42368,42370,42372],{"class":1738,"line":1755},[1736,42369,41644],{"class":1912},[1736,42371,42020],{"class":1918},[1736,42373,1939],{"class":1912},[1736,42375,42376,42379,42381],{"class":1738,"line":1761},[1736,42377,42378],{"class":1912},"    rtl: ",[1736,42380,27129],{"class":1918},[1736,42382,1939],{"class":1912},[1736,42384,42385,42387],{"class":1738,"line":1767},[1736,42386,41654],{"class":1912},[1736,42388,21380],{"class":1918},[1736,42390,42391],{"class":1738,"line":1772},[1736,42392,1971],{"class":1912},[1736,42394,42395],{"class":1738,"line":1778},[1736,42396,1976],{"class":1912},[11,42398,42399],{},"If you don't like the progress bar at all you can disable it completely in the nuxt.config.js",[299,42401,42402],{"className":5635,"code":41588,"language":5637,"meta":307,"style":307},[179,42403,42404,42412,42418],{"__ignoreMap":307},[1736,42405,42406,42408,42410],{"class":1738,"line":1739},[1736,42407,6632],{"class":4866},[1736,42409,30438],{"class":4866},[1736,42411,4914],{"class":1912},[1736,42413,42414,42416],{"class":1738,"line":748},[1736,42415,41603],{"class":1912},[1736,42417,35177],{"class":1918},[1736,42419,42420],{"class":1738,"line":756},[1736,42421,1976],{"class":1912},[11,42423,42424],{},"You can also disable it for a specific page.",[299,42426,42428],{"className":21974,"code":42427,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Ch1>About Page\u003C/h1>\n\u003C/template>\n\n\u003Cscript>\n  export default {\n    loading: false\n  }\n\u003C/script>\n",[179,42429,42430,42438,42451,42459,42463,42471,42479,42485,42489],{"__ignoreMap":307},[1736,42431,42432,42434,42436],{"class":1738,"line":1739},[1736,42433,6657],{"class":1912},[1736,42435,21985],{"class":6696},[1736,42437,6663],{"class":1912},[1736,42439,42440,42442,42444,42447,42449],{"class":1738,"line":748},[1736,42441,7020],{"class":1912},[1736,42443,30550],{"class":6696},[1736,42445,42446],{"class":1912},">About Page\u003C/",[1736,42448,30550],{"class":6696},[1736,42450,6663],{"class":1912},[1736,42452,42453,42455,42457],{"class":1738,"line":756},[1736,42454,8105],{"class":1912},[1736,42456,21985],{"class":6696},[1736,42458,6663],{"class":1912},[1736,42460,42461],{"class":1738,"line":1755},[1736,42462,1747],{"emptyLinePlaceholder":790},[1736,42464,42465,42467,42469],{"class":1738,"line":1761},[1736,42466,6657],{"class":1912},[1736,42468,21869],{"class":6696},[1736,42470,6663],{"class":1912},[1736,42472,42473,42475,42477],{"class":1738,"line":1767},[1736,42474,38306],{"class":4866},[1736,42476,30438],{"class":4866},[1736,42478,4914],{"class":1912},[1736,42480,42481,42483],{"class":1738,"line":1772},[1736,42482,41787],{"class":1912},[1736,42484,35177],{"class":1918},[1736,42486,42487],{"class":1738,"line":1778},[1736,42488,1971],{"class":1912},[1736,42490,42491,42493,42495],{"class":1738,"line":1784},[1736,42492,8105],{"class":1912},[1736,42494,21869],{"class":6696},[1736,42496,6663],{"class":1912},[11,42498,42499],{},"And if you want to show loading but don't like the default loading component you can of course create your own. In the components folder create a component called loading.vue.",[299,42501,42503],{"className":21974,"code":42502,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv v-if=\"loading\" class=\"loading-page\">\n    \u003Cp>Loading...\u003C/p>\n  \u003C/div>\n\u003C/template>\n\u003Cscript>\n  export default {\n    data: () => ({\n      loading: false\n    }),\n    methods: {\n      start() {\n        this.loading = true\n      },\n      finish() {\n        this.loading = false\n      }\n    }\n  }\n\u003C/script>\n\u003Cstyle scoped>\n  .loading-page {\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    background: yellowgreen;\n    text-align: center;\n    padding-top: 100px;\n    color: red;\n    font-size: 80px;\n    font-family: sans-serif;\n  }\n\u003C/style>\n",[179,42504,42505,42513,42533,42545,42553,42561,42569,42577,42587,42594,42599,42603,42610,42620,42624,42631,42641,42645,42649,42653,42661,42671,42678,42690,42701,42712,42725,42738,42750,42761,42774,42784,42798,42810,42814],{"__ignoreMap":307},[1736,42506,42507,42509,42511],{"class":1738,"line":1739},[1736,42508,6657],{"class":1912},[1736,42510,21985],{"class":6696},[1736,42512,6663],{"class":1912},[1736,42514,42515,42517,42519,42521,42523,42525,42527,42529,42531],{"class":1738,"line":748},[1736,42516,7020],{"class":1912},[1736,42518,6697],{"class":6696},[1736,42520,39261],{"class":2674},[1736,42522,5062],{"class":1912},[1736,42524,41698],{"class":1935},[1736,42526,36491],{"class":2674},[1736,42528,5062],{"class":1912},[1736,42530,41928],{"class":1935},[1736,42532,6663],{"class":1912},[1736,42534,42535,42537,42539,42541,42543],{"class":1738,"line":756},[1736,42536,6693],{"class":1912},[1736,42538,11],{"class":6696},[1736,42540,41709],{"class":1912},[1736,42542,11],{"class":6696},[1736,42544,6663],{"class":1912},[1736,42546,42547,42549,42551],{"class":1738,"line":1755},[1736,42548,8096],{"class":1912},[1736,42550,6697],{"class":6696},[1736,42552,6663],{"class":1912},[1736,42554,42555,42557,42559],{"class":1738,"line":1761},[1736,42556,8105],{"class":1912},[1736,42558,21985],{"class":6696},[1736,42560,6663],{"class":1912},[1736,42562,42563,42565,42567],{"class":1738,"line":1767},[1736,42564,6657],{"class":1912},[1736,42566,21869],{"class":6696},[1736,42568,6663],{"class":1912},[1736,42570,42571,42573,42575],{"class":1738,"line":1772},[1736,42572,38306],{"class":4866},[1736,42574,30438],{"class":4866},[1736,42576,4914],{"class":1912},[1736,42578,42579,42581,42583,42585],{"class":1738,"line":1778},[1736,42580,35160],{"class":2674},[1736,42582,13948],{"class":1912},[1736,42584,7013],{"class":4866},[1736,42586,41782],{"class":1912},[1736,42588,42589,42592],{"class":1738,"line":1784},[1736,42590,42591],{"class":1912},"      loading: ",[1736,42593,35177],{"class":1918},[1736,42595,42596],{"class":1738,"line":1790},[1736,42597,42598],{"class":1912},"    }),\n",[1736,42600,42601],{"class":1738,"line":1796},[1736,42602,35190],{"class":1912},[1736,42604,42605,42608],{"class":1738,"line":2353},[1736,42606,42607],{"class":2674},"      start",[1736,42609,6680],{"class":1912},[1736,42611,42612,42614,42616,42618],{"class":1738,"line":2358},[1736,42613,35202],{"class":1918},[1736,42615,41813],{"class":1912},[1736,42617,5062],{"class":4866},[1736,42619,41818],{"class":1918},[1736,42621,42622],{"class":1738,"line":2364},[1736,42623,15708],{"class":1912},[1736,42625,42626,42629],{"class":1738,"line":2370},[1736,42627,42628],{"class":2674},"      finish",[1736,42630,6680],{"class":1912},[1736,42632,42633,42635,42637,42639],{"class":1738,"line":2376},[1736,42634,35202],{"class":1918},[1736,42636,41813],{"class":1912},[1736,42638,5062],{"class":4866},[1736,42640,41840],{"class":1918},[1736,42642,42643],{"class":1738,"line":2381},[1736,42644,14448],{"class":1912},[1736,42646,42647],{"class":1738,"line":2387},[1736,42648,9853],{"class":1912},[1736,42650,42651],{"class":1738,"line":2393},[1736,42652,1971],{"class":1912},[1736,42654,42655,42657,42659],{"class":1738,"line":2398},[1736,42656,8105],{"class":1912},[1736,42658,21869],{"class":6696},[1736,42660,6663],{"class":1912},[1736,42662,42663,42665,42667,42669],{"class":1738,"line":2404},[1736,42664,6657],{"class":1912},[1736,42666,2011],{"class":6696},[1736,42668,41977],{"class":2674},[1736,42670,6663],{"class":1912},[1736,42672,42673,42676],{"class":1738,"line":6959},[1736,42674,42675],{"class":2674},"  .loading-page",[1736,42677,4914],{"class":1912},[1736,42679,42680,42683,42685,42688],{"class":1738,"line":7296},[1736,42681,42682],{"class":1918},"    position",[1736,42684,3065],{"class":1912},[1736,42686,42687],{"class":1918},"fixed",[1736,42689,7682],{"class":1912},[1736,42691,42692,42695,42697,42699],{"class":1738,"line":7305},[1736,42693,42694],{"class":1918},"    top",[1736,42696,3065],{"class":1912},[1736,42698,1290],{"class":1918},[1736,42700,7682],{"class":1912},[1736,42702,42703,42706,42708,42710],{"class":1738,"line":7310},[1736,42704,42705],{"class":1918},"    left",[1736,42707,3065],{"class":1912},[1736,42709,1290],{"class":1918},[1736,42711,7682],{"class":1912},[1736,42713,42714,42717,42719,42721,42723],{"class":1738,"line":9659},[1736,42715,42716],{"class":1918},"    width",[1736,42718,3065],{"class":1912},[1736,42720,6372],{"class":1918},[1736,42722,42171],{"class":4866},[1736,42724,7682],{"class":1912},[1736,42726,42727,42730,42732,42734,42736],{"class":1738,"line":9680},[1736,42728,42729],{"class":1918},"    height",[1736,42731,3065],{"class":1912},[1736,42733,6372],{"class":1918},[1736,42735,42171],{"class":4866},[1736,42737,7682],{"class":1912},[1736,42739,42740,42743,42745,42748],{"class":1738,"line":9699},[1736,42741,42742],{"class":1918},"    background",[1736,42744,3065],{"class":1912},[1736,42746,42747],{"class":1918},"yellowgreen",[1736,42749,7682],{"class":1912},[1736,42751,42752,42755,42757,42759],{"class":1738,"line":9704},[1736,42753,42754],{"class":1918},"    text-align",[1736,42756,3065],{"class":1912},[1736,42758,38395],{"class":1918},[1736,42760,7682],{"class":1912},[1736,42762,42763,42766,42768,42770,42772],{"class":1738,"line":9715},[1736,42764,42765],{"class":1918},"    padding-top",[1736,42767,3065],{"class":1912},[1736,42769,6372],{"class":1918},[1736,42771,38383],{"class":4866},[1736,42773,7682],{"class":1912},[1736,42775,42776,42778,42780,42782],{"class":1738,"line":9727},[1736,42777,38443],{"class":1918},[1736,42779,3065],{"class":1912},[1736,42781,36849],{"class":1918},[1736,42783,7682],{"class":1912},[1736,42785,42786,42789,42791,42794,42796],{"class":1738,"line":9739},[1736,42787,42788],{"class":1918},"    font-size",[1736,42790,3065],{"class":1912},[1736,42792,42793],{"class":1918},"80",[1736,42795,38383],{"class":4866},[1736,42797,7682],{"class":1912},[1736,42799,42800,42803,42805,42808],{"class":1738,"line":9750},[1736,42801,42802],{"class":1918},"    font-family",[1736,42804,3065],{"class":1912},[1736,42806,42807],{"class":1918},"sans-serif",[1736,42809,7682],{"class":1912},[1736,42811,42812],{"class":1738,"line":9761},[1736,42813,1971],{"class":1912},[1736,42815,42816,42818,42820],{"class":1738,"line":9767},[1736,42817,8105],{"class":1912},[1736,42819,2011],{"class":6696},[1736,42821,6663],{"class":1912},[11,42823,42824],{},"Then in the nuxt.config.js file add the component to the loading property.",[299,42826,42828],{"className":5635,"code":42827,"language":5637,"meta":307,"style":307},"export default {\n  loading: '~/components/loading.vue'\n}\n",[179,42829,42830,42838,42845],{"__ignoreMap":307},[1736,42831,42832,42834,42836],{"class":1738,"line":1739},[1736,42833,6632],{"class":4866},[1736,42835,30438],{"class":4866},[1736,42837,4914],{"class":1912},[1736,42839,42840,42842],{"class":1738,"line":748},[1736,42841,41603],{"class":1912},[1736,42843,42844],{"class":1935},"'~/components/loading.vue'\n",[1736,42846,42847],{"class":1738,"line":756},[1736,42848,1976],{"class":1912},[11,42850,42851],{},"And that's it you now have your own custom loading component for when changing routes and your content is loading.",[2011,42853,42854],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":307,"searchDepth":748,"depth":748,"links":42856},[],"2020-04-17","photo-1516906158245-7fafb29df119?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop",{},"/blog/nuxt-loading-progress-bar",{"title":42297,"description":42302},"blog/nuxt-loading-progress-bar",[5239],"qnN9RVSZxDcWQkY6oIFeZOB1cyXXMDM4p_XSj_yLa-g",{"id":42866,"title":42867,"body":42868,"canonical":788,"date":44125,"description":44126,"extension":786,"featured":787,"image":44127,"meta":44128,"navigation":790,"ogimage":788,"path":44129,"provider":5235,"published":787,"seo":44130,"stem":44131,"tags":44132,"url":788,"__hash__":44133},"blog/blog/nuxt-middleware.md","Understanding Nuxt Middleware",{"type":8,"value":42869,"toc":44105},[42870,42873,42882,42885,42887,42891,42894,42898,42905,42916,43003,43007,43013,43059,43063,43066,43087,43091,43098,43124,43131,43137,43141,43144,43172,43174,43178,43181,43188,43267,43271,43274,43436,43443,43584,43587,43591,43594,43717,43721,43724,43763,43767,43770,43796,43799,43878,43882,43885,43888,43891,43894,43896,43900,43903,43907,43910,43986,43989,44008,44011,44059,44062,44064,44066,44102],[11,42871,42872],{},"Middleware lets you define custom functions that can be run before rendering either a page or a group of pages which we call layouts. In universal mode, middlewares will be called once on server-side (on the first request to the Nuxt app, e.g. when directly accessing the app or refreshing the page) and on the client-side when navigating to further routes.",[11,42874,42875,42876,42881],{},"When SSR is false, middlewares will be called on the client-side in both situations. Middlewares are executed in series in this order, nuxt config, in the order within the file, followed by matched layouts and then matched pages. A middleware receives ",[15,42877,42880],{"href":42878,"rel":42879},"https://nuxtjs.org/docs/2.x/concepts/context-helpers",[19],"the context"," as the first argument.",[11,42883,42884],{},"In Nuxt we have 3 different ways of writing Middleware. We have Router Middleware, Named Middleware and Anonymous Middleware. Let's take a look at them all.",[731,42886],{},[23,42888,42890],{"id":42889},"router-middleware","Router Middleware",[11,42892,42893],{},"We can create a middleware that adds a class to the body using the store and with router middleware we can set the class before we enter the route. We can then use css to style the class depending on the page we are on.",[138,42895,42897],{"id":42896},"setting-up-the-store-for-router-middleware","Setting up the Store for Router Middleware",[11,42899,42900,42901,42904],{},"We first need to set up our store which we can do by simply adding an ",[179,42902,42903],{},"index.js"," file in the store directory. This will tell Nuxt you want to use the store and it will then be automatically activated and included in your bundle.",[11,42906,42907,42908,42911,42912,42915],{},"In the store we can export state with bodyClass set to an empty string. We then need to setup a mutation called ",[179,42909,42910],{},"SET_CLASS"," which passes in the state and the bodyClass and sets the ",[179,42913,42914],{},"state.bodyClass"," equal to the new value of bodyClass.",[299,42917,42920],{"className":8734,"code":42918,"filename":42919,"language":8736,"meta":307,"style":307},"export const state = () => ({\n  bodyClass: ''\n})\n\nexport const mutations = {\n  SetClass(state, bodyClass) {\n    state.bodyClass = bodyClass\n  }\n}\n","store/index.js",[179,42921,42922,42939,42947,42951,42955,42968,42985,42995,42999],{"__ignoreMap":307},[1736,42923,42924,42926,42928,42931,42933,42935,42937],{"class":1738,"line":1739},[1736,42925,6632],{"class":4866},[1736,42927,7002],{"class":4866},[1736,42929,42930],{"class":2674}," state",[1736,42932,4911],{"class":4866},[1736,42934,7010],{"class":1912},[1736,42936,7013],{"class":4866},[1736,42938,41782],{"class":1912},[1736,42940,42941,42944],{"class":1738,"line":748},[1736,42942,42943],{"class":1912},"  bodyClass: ",[1736,42945,42946],{"class":1935},"''\n",[1736,42948,42949],{"class":1738,"line":756},[1736,42950,10582],{"class":1912},[1736,42952,42953],{"class":1738,"line":1755},[1736,42954,1747],{"emptyLinePlaceholder":790},[1736,42956,42957,42959,42961,42964,42966],{"class":1738,"line":1761},[1736,42958,6632],{"class":4866},[1736,42960,7002],{"class":4866},[1736,42962,42963],{"class":1918}," mutations",[1736,42965,4911],{"class":4866},[1736,42967,4914],{"class":1912},[1736,42969,42970,42973,42975,42978,42980,42983],{"class":1738,"line":1767},[1736,42971,42972],{"class":2674},"  SetClass",[1736,42974,7751],{"class":1912},[1736,42976,42977],{"class":5036},"state",[1736,42979,829],{"class":1912},[1736,42981,42982],{"class":5036},"bodyClass",[1736,42984,7246],{"class":1912},[1736,42986,42987,42990,42992],{"class":1738,"line":1772},[1736,42988,42989],{"class":1912},"    state.bodyClass ",[1736,42991,5062],{"class":4866},[1736,42993,42994],{"class":1912}," bodyClass\n",[1736,42996,42997],{"class":1738,"line":1778},[1736,42998,1971],{"class":1912},[1736,43000,43001],{"class":1738,"line":1784},[1736,43002,1976],{"class":1912},[138,43004,43006],{"id":43005},"creating-our-router-middleware","Creating our Router Middleware",[11,43008,43009,43010,43012],{},"In the middleware folder we can create a file which exports a function passing in the store and route from the context. Using the store we commit the mutation of ",[179,43011,42910],{}," and pass in the name of the route which will then be the value of our new bodyClass.",[299,43014,43017],{"className":8734,"code":43015,"filename":43016,"language":8736,"meta":307,"style":307},"export default function({ store, route }) {\n  store.commit('SetClass', route.name)\n}\n","middleware/class.js",[179,43018,43019,43039,43055],{"__ignoreMap":307},[1736,43020,43021,43023,43025,43027,43029,43032,43034,43037],{"class":1738,"line":1739},[1736,43022,6632],{"class":4866},[1736,43024,30438],{"class":4866},[1736,43026,6674],{"class":4866},[1736,43028,7233],{"class":1912},[1736,43030,43031],{"class":5036},"store",[1736,43033,829],{"class":1912},[1736,43035,43036],{"class":5036},"route",[1736,43038,31433],{"class":1912},[1736,43040,43041,43044,43047,43049,43052],{"class":1738,"line":748},[1736,43042,43043],{"class":1912},"  store.",[1736,43045,43046],{"class":2674},"commit",[1736,43048,7751],{"class":1912},[1736,43050,43051],{"class":1935},"'SetClass'",[1736,43053,43054],{"class":1912},", route.name)\n",[1736,43056,43057],{"class":1738,"line":756},[1736,43058,1976],{"class":1912},[138,43060,43062],{"id":43061},"adding-our-middleware-to-the-router-property","Adding our Middleware to the Router Property",[11,43064,43065],{},"As we want this middleware to be called on every route change we need to add the middleware to the router property in our nuxt config. Using the middleware property we add in the name of the middleware we want to use which in this case is the middleware called class.",[299,43067,43071],{"className":43068,"code":43069,"filename":33252,"language":43070,"meta":307,"style":307},"language-ja shiki shiki-themes github-light github-dark","router: {\n  middleware: [ 'class' ];\n}\n","ja",[179,43072,43073,43078,43083],{"__ignoreMap":307},[1736,43074,43075],{"class":1738,"line":1739},[1736,43076,43077],{},"router: {\n",[1736,43079,43080],{"class":1738,"line":748},[1736,43081,43082],{},"  middleware: [ 'class' ];\n",[1736,43084,43085],{"class":1738,"line":756},[1736,43086,1976],{},[138,43088,43090],{"id":43089},"adding-the-class-to-our-layout","Adding the Class to our Layout",[11,43092,43093,43094,43097],{},"In our default layout we need to add our new body class. We do that by adding a dynamic class and passing the ",[179,43095,43096],{},"store.state.bodyClass",". Here we add the text body before it just for clarity.",[299,43099,43102],{"className":21974,"code":43100,"filename":43101,"language":21976,"meta":307,"style":307},"\u003Cdiv :class=\"[`body-${$store.state.bodyClass}`]\">\u003C/div>\n","layouts/default.vue",[179,43103,43104],{"__ignoreMap":307},[1736,43105,43106,43108,43110,43113,43115,43118,43120,43122],{"class":1738,"line":1739},[1736,43107,6657],{"class":1912},[1736,43109,6697],{"class":6696},[1736,43111,43112],{"class":2674}," :class",[1736,43114,5062],{"class":1912},[1736,43116,43117],{"class":1935},"\"[`body-${$store.state.bodyClass}`]\"",[1736,43119,8252],{"class":1912},[1736,43121,6697],{"class":6696},[1736,43123,6663],{"class":1912},[11,43125,43126,43127,43130],{},"In our browser if we inspect the code we will see we have a body class called ",[179,43128,43129],{},"body-index",". Index is being added to the body class by the middleware by reading the route name before it changes the route.",[11,43132,43133,43134,891],{},"If we add another page, for example an about page, then that page will have a class of ",[179,43135,43136],{},"body-about",[138,43138,43140],{"id":43139},"adding-the-styles-for-our-page","Adding the Styles for our Page",[11,43142,43143],{},"Then all you need to do is add styles to that class for each route so you can have a different background color for each page, or different font size or whatever you want.",[299,43145,43148],{"className":4857,"code":43146,"filename":43147,"language":4859,"meta":307,"style":307},".body-about {\n  background-color: red;\n}\n","assets/main.css",[179,43149,43150,43157,43168],{"__ignoreMap":307},[1736,43151,43152,43155],{"class":1738,"line":1739},[1736,43153,43154],{"class":2674},".body-about",[1736,43156,4914],{"class":1912},[1736,43158,43159,43162,43164,43166],{"class":1738,"line":748},[1736,43160,43161],{"class":1918},"  background-color",[1736,43163,3065],{"class":1912},[1736,43165,36849],{"class":1918},[1736,43167,7682],{"class":1912},[1736,43169,43170],{"class":1738,"line":756},[1736,43171,1976],{"class":1912},[731,43173],{},[23,43175,43177],{"id":43176},"named-middleware","Named Middleware",[11,43179,43180],{},"We can use named middleware to check if a user is logged in and redirect them if they aren't.",[11,43182,43183,43184,43187],{},"In this middleware we pass in redirect and store from the context. Then we check to see if the variable ",[179,43185,43186],{},"isAuthenticated"," is not true, meaning the user is not logged in so we redirect them to the page called login.",[299,43189,43192],{"className":8734,"code":43190,"filename":43191,"language":8736,"meta":307,"style":307},"export default function({ redirect, store }) {\n  const isAuthenticated = store.state.auth.user ? true : false\n  if (!isAuthenticated) {\n    redirect({ name: 'auth' })\n  }\n}\n","middleware/auth.js",[179,43193,43194,43213,43235,43246,43259,43263],{"__ignoreMap":307},[1736,43195,43196,43198,43200,43202,43204,43207,43209,43211],{"class":1738,"line":1739},[1736,43197,6632],{"class":4866},[1736,43199,30438],{"class":4866},[1736,43201,6674],{"class":4866},[1736,43203,7233],{"class":1912},[1736,43205,43206],{"class":5036},"redirect",[1736,43208,829],{"class":1912},[1736,43210,43031],{"class":5036},[1736,43212,31433],{"class":1912},[1736,43214,43215,43217,43220,43222,43225,43227,43230,43233],{"class":1738,"line":748},[1736,43216,7824],{"class":4866},[1736,43218,43219],{"class":1918}," isAuthenticated",[1736,43221,4911],{"class":4866},[1736,43223,43224],{"class":1912}," store.state.auth.user ",[1736,43226,23776],{"class":4866},[1736,43228,43229],{"class":1918}," true",[1736,43231,43232],{"class":4866}," :",[1736,43234,41840],{"class":1918},[1736,43236,43237,43239,43241,43243],{"class":1738,"line":756},[1736,43238,9324],{"class":4866},[1736,43240,1095],{"class":1912},[1736,43242,1690],{"class":4866},[1736,43244,43245],{"class":1912},"isAuthenticated) {\n",[1736,43247,43248,43251,43254,43257],{"class":1738,"line":1755},[1736,43249,43250],{"class":2674},"    redirect",[1736,43252,43253],{"class":1912},"({ name: ",[1736,43255,43256],{"class":1935},"'auth'",[1736,43258,10691],{"class":1912},[1736,43260,43261],{"class":1738,"line":1761},[1736,43262,1971],{"class":1912},[1736,43264,43265],{"class":1738,"line":1767},[1736,43266,1976],{"class":1912},[138,43268,43270],{"id":43269},"setting-up-the-login-form","Setting up the Login Form",[11,43272,43273],{},"We can then create a simple login form that asks the user to submit their username and password and a button that calls a login method when clicked. We also make the button disabled if there is no username or password.",[299,43275,43278],{"className":8734,"code":43276,"filename":43277,"language":8736,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cform @submit.prevent>\n      \u003Clabel for=\"username\">Username\u003C/label>\n      \u003Cinput v-model=\"user.username\" id=\"username\" type=\"text\">\n      \u003Clabel for=\"password\">Password\u003C/label>\n      \u003Cinput v-model=\"user.password\" id=\"password\" type=\"text\">\n      \u003Cbutton @click=\"login\" :disabled=\"!user.username || !user.password\">Login\u003C/button>\n    \u003C/form>\n  \u003C/div>\n\u003C/template>\n","pages/auth.vue",[179,43279,43280,43288,43296,43306,43326,43356,43376,43403,43421,43426,43431],{"__ignoreMap":307},[1736,43281,43282,43284,43286],{"class":1738,"line":1739},[1736,43283,6657],{"class":1912},[1736,43285,21985],{"class":6696},[1736,43287,6663],{"class":1912},[1736,43289,43290,43292,43294],{"class":1738,"line":748},[1736,43291,7020],{"class":1912},[1736,43293,6697],{"class":6696},[1736,43295,6663],{"class":1912},[1736,43297,43298,43300,43303],{"class":1738,"line":756},[1736,43299,6693],{"class":1912},[1736,43301,43302],{"class":6696},"form",[1736,43304,43305],{"class":30490}," @submit.prevent>\n",[1736,43307,43308,43311,43314,43316,43319,43322,43324],{"class":1738,"line":1755},[1736,43309,43310],{"class":30490},"      \u003Clabel",[1736,43312,43313],{"class":2674}," for",[1736,43315,5062],{"class":4866},[1736,43317,43318],{"class":1935},"\"username\"",[1736,43320,43321],{"class":1912},">Username\u003C/",[1736,43323,26907],{"class":6696},[1736,43325,6663],{"class":1912},[1736,43327,43328,43330,43333,43336,43338,43341,43344,43346,43348,43350,43352,43354],{"class":1738,"line":1761},[1736,43329,6710],{"class":1912},[1736,43331,43332],{"class":6696},"input",[1736,43334,43335],{"class":2674}," v-model",[1736,43337,5062],{"class":4866},[1736,43339,43340],{"class":1935},"\"user.username\"",[1736,43342,43343],{"class":2674}," id",[1736,43345,5062],{"class":4866},[1736,43347,43318],{"class":1935},[1736,43349,6635],{"class":2674},[1736,43351,5062],{"class":4866},[1736,43353,17767],{"class":1935},[1736,43355,6663],{"class":1912},[1736,43357,43358,43360,43362,43364,43366,43369,43372,43374],{"class":1738,"line":1767},[1736,43359,6710],{"class":1912},[1736,43361,26907],{"class":6696},[1736,43363,43313],{"class":2674},[1736,43365,5062],{"class":4866},[1736,43367,43368],{"class":1935},"\"password\"",[1736,43370,43371],{"class":1912},">Password\u003C/",[1736,43373,26907],{"class":6696},[1736,43375,6663],{"class":1912},[1736,43377,43378,43380,43382,43384,43386,43389,43391,43393,43395,43397,43399,43401],{"class":1738,"line":1772},[1736,43379,6710],{"class":1912},[1736,43381,43332],{"class":6696},[1736,43383,43335],{"class":2674},[1736,43385,5062],{"class":4866},[1736,43387,43388],{"class":1935},"\"user.password\"",[1736,43390,43343],{"class":2674},[1736,43392,5062],{"class":4866},[1736,43394,43368],{"class":1935},[1736,43396,6635],{"class":2674},[1736,43398,5062],{"class":4866},[1736,43400,17767],{"class":1935},[1736,43402,6663],{"class":1912},[1736,43404,43405,43407,43409,43412,43415,43418],{"class":1738,"line":1778},[1736,43406,6710],{"class":1912},[1736,43408,14849],{"class":6696},[1736,43410,43411],{"class":30490}," @click=\"login\"",[1736,43413,43414],{"class":30490}," :disabled=\"!user.username",[1736,43416,43417],{"class":30490}," ||",[1736,43419,43420],{"class":30490}," !user.password\">Login\u003C/button>\n",[1736,43422,43423],{"class":1738,"line":1784},[1736,43424,43425],{"class":30490},"    \u003C/form>\n",[1736,43427,43428],{"class":1738,"line":1790},[1736,43429,43430],{"class":30490},"  \u003C/div>\n",[1736,43432,43433],{"class":1738,"line":1796},[1736,43434,43435],{"class":30490},"\u003C/template>\n",[11,43437,43438,43439,43442],{},"Then in our script tag we need to add our data for our user and set the username and password to an empty string. This value will get updated thanks to ",[179,43440,43441],{},"v-modal"," that we use in the form. We then need a login method which will commit to the store the value of our username and password once the login button has been clicked.",[299,43444,43446],{"className":8734,"code":43445,"filename":43277,"language":8736,"meta":307,"style":307},"\u003Cscript>\nexport default {\n  data () {\n    return {\n      user: {\n        username: '',\n        password: ''\n      }\n    }\n  }\n  methods: {\n    login(){\n      this.$store.commit('auth/setUser', this.user.username)\n      this.$store.commit('auth/setPass', this.user.password)\n    }\n  }\n}\n\u003C/script>\n",[179,43447,43448,43456,43460,43468,43473,43480,43491,43500,43504,43508,43512,43516,43523,43544,43564,43568,43572,43576],{"__ignoreMap":307},[1736,43449,43450,43452,43454],{"class":1738,"line":1739},[1736,43451,6657],{"class":1912},[1736,43453,21869],{"class":6696},[1736,43455,6663],{"class":1912},[1736,43457,43458],{"class":1738,"line":748},[1736,43459,21876],{"class":1912},[1736,43461,43462,43465],{"class":1738,"line":756},[1736,43463,43464],{"class":2674},"  data",[1736,43466,43467],{"class":1912}," () {\n",[1736,43469,43470],{"class":1738,"line":1755},[1736,43471,43472],{"class":1912},"    return {\n",[1736,43474,43475,43478],{"class":1738,"line":1761},[1736,43476,43477],{"class":2674},"      user",[1736,43479,1922],{"class":1912},[1736,43481,43482,43485,43487,43489],{"class":1738,"line":1767},[1736,43483,43484],{"class":2674},"        username",[1736,43486,3065],{"class":1912},[1736,43488,34884],{"class":1935},[1736,43490,1939],{"class":1912},[1736,43492,43493,43496,43498],{"class":1738,"line":1772},[1736,43494,43495],{"class":2674},"        password",[1736,43497,3065],{"class":1912},[1736,43499,42946],{"class":1935},[1736,43501,43502],{"class":1738,"line":1778},[1736,43503,14448],{"class":1912},[1736,43505,43506],{"class":1738,"line":1784},[1736,43507,9853],{"class":1912},[1736,43509,43510],{"class":1738,"line":1790},[1736,43511,1971],{"class":1912},[1736,43513,43514],{"class":1738,"line":1796},[1736,43515,21881],{"class":1912},[1736,43517,43518,43521],{"class":1738,"line":2353},[1736,43519,43520],{"class":2674},"    login",[1736,43522,15292],{"class":1912},[1736,43524,43525,43527,43530,43532,43534,43537,43539,43541],{"class":1738,"line":2358},[1736,43526,41810],{"class":1918},[1736,43528,43529],{"class":1912},".$store.",[1736,43531,43046],{"class":2674},[1736,43533,7751],{"class":1912},[1736,43535,43536],{"class":1935},"'auth/setUser'",[1736,43538,829],{"class":1912},[1736,43540,35213],{"class":1918},[1736,43542,43543],{"class":1912},".user.username)\n",[1736,43545,43546,43548,43550,43552,43554,43557,43559,43561],{"class":1738,"line":2364},[1736,43547,41810],{"class":1918},[1736,43549,43529],{"class":1912},[1736,43551,43046],{"class":2674},[1736,43553,7751],{"class":1912},[1736,43555,43556],{"class":1935},"'auth/setPass'",[1736,43558,829],{"class":1912},[1736,43560,35213],{"class":1918},[1736,43562,43563],{"class":1912},".user.password)\n",[1736,43565,43566],{"class":1738,"line":2370},[1736,43567,9853],{"class":1912},[1736,43569,43570],{"class":1738,"line":2376},[1736,43571,1971],{"class":1912},[1736,43573,43574],{"class":1738,"line":2381},[1736,43575,1976],{"class":1912},[1736,43577,43578,43580,43582],{"class":1738,"line":2387},[1736,43579,8105],{"class":1912},[1736,43581,21869],{"class":6696},[1736,43583,6663],{"class":1912},[11,43585,43586],{},"And we need to create our login method which will set the value of our user and the value of our password in the store by passing in the data property of the user which uses v-model to bind the input to the data.",[138,43588,43590],{"id":43589},"setting-up-the-store-for-named-middleware","Setting up the Store for Named Middleware",[11,43592,43593],{},"We first set the state of the user and pass to be null and then create a setUser mutation passing in the state of user and making the state equal to the new user value passed in and we do the same for the password.",[299,43595,43598],{"className":8734,"code":43596,"filename":43597,"language":8736,"meta":307,"style":307},"export const state = () => ({\n  user: null,\n  pass: null\n})\n\nexport const mutations = {\n  setUser(state, user) {\n    state.user = user\n  },\n  setPass(state, pass) {\n    state.pass = pass\n  }\n}\n","store/auth.js",[179,43599,43600,43616,43625,43633,43637,43641,43653,43669,43679,43683,43699,43709,43713],{"__ignoreMap":307},[1736,43601,43602,43604,43606,43608,43610,43612,43614],{"class":1738,"line":1739},[1736,43603,6632],{"class":4866},[1736,43605,7002],{"class":4866},[1736,43607,42930],{"class":2674},[1736,43609,4911],{"class":4866},[1736,43611,7010],{"class":1912},[1736,43613,7013],{"class":4866},[1736,43615,41782],{"class":1912},[1736,43617,43618,43621,43623],{"class":1738,"line":748},[1736,43619,43620],{"class":1912},"  user: ",[1736,43622,18634],{"class":1918},[1736,43624,1939],{"class":1912},[1736,43626,43627,43630],{"class":1738,"line":756},[1736,43628,43629],{"class":1912},"  pass: ",[1736,43631,43632],{"class":1918},"null\n",[1736,43634,43635],{"class":1738,"line":1755},[1736,43636,10582],{"class":1912},[1736,43638,43639],{"class":1738,"line":1761},[1736,43640,1747],{"emptyLinePlaceholder":790},[1736,43642,43643,43645,43647,43649,43651],{"class":1738,"line":1767},[1736,43644,6632],{"class":4866},[1736,43646,7002],{"class":4866},[1736,43648,42963],{"class":1918},[1736,43650,4911],{"class":4866},[1736,43652,4914],{"class":1912},[1736,43654,43655,43658,43660,43662,43664,43667],{"class":1738,"line":1772},[1736,43656,43657],{"class":2674},"  setUser",[1736,43659,7751],{"class":1912},[1736,43661,42977],{"class":5036},[1736,43663,829],{"class":1912},[1736,43665,43666],{"class":5036},"user",[1736,43668,7246],{"class":1912},[1736,43670,43671,43674,43676],{"class":1738,"line":1778},[1736,43672,43673],{"class":1912},"    state.user ",[1736,43675,5062],{"class":4866},[1736,43677,43678],{"class":1912}," user\n",[1736,43680,43681],{"class":1738,"line":1784},[1736,43682,4929],{"class":1912},[1736,43684,43685,43688,43690,43692,43694,43697],{"class":1738,"line":1790},[1736,43686,43687],{"class":2674},"  setPass",[1736,43689,7751],{"class":1912},[1736,43691,42977],{"class":5036},[1736,43693,829],{"class":1912},[1736,43695,43696],{"class":5036},"pass",[1736,43698,7246],{"class":1912},[1736,43700,43701,43704,43706],{"class":1738,"line":1796},[1736,43702,43703],{"class":1912},"    state.pass ",[1736,43705,5062],{"class":4866},[1736,43707,43708],{"class":1912}," pass\n",[1736,43710,43711],{"class":1738,"line":2353},[1736,43712,1971],{"class":1912},[1736,43714,43715],{"class":1738,"line":2358},[1736,43716,1976],{"class":1912},[138,43718,43720],{"id":43719},"adding-named-middleware-to-our-authenticated-page","Adding Named Middleware to our Authenticated Page",[11,43722,43723],{},"In the authenticated page we use the middleware property to add the auth middleware. This means the middleware will be called before entering this page.",[299,43725,43728],{"className":8734,"code":43726,"filename":43727,"language":8736,"meta":307,"style":307},"\u003Cscript>\nexport default {\n  middleware: 'auth',\n}\n\u003C/script>\n","pages/admin.vue",[179,43729,43730,43738,43742,43751,43755],{"__ignoreMap":307},[1736,43731,43732,43734,43736],{"class":1738,"line":1739},[1736,43733,6657],{"class":1912},[1736,43735,21869],{"class":6696},[1736,43737,6663],{"class":1912},[1736,43739,43740],{"class":1738,"line":748},[1736,43741,21876],{"class":1912},[1736,43743,43744,43747,43749],{"class":1738,"line":756},[1736,43745,43746],{"class":1912},"  middleware: ",[1736,43748,43256],{"class":1935},[1736,43750,1939],{"class":1912},[1736,43752,43753],{"class":1738,"line":1755},[1736,43754,1976],{"class":1912},[1736,43756,43757,43759,43761],{"class":1738,"line":1761},[1736,43758,8105],{"class":1912},[1736,43760,21869],{"class":6696},[1736,43762,6663],{"class":1912},[138,43764,43766],{"id":43765},"creating-a-logout-method","Creating a Logout Method",[11,43768,43769],{},"We can also create a logout button so we can properly test that it is working. The logout button when clicked will call a method called logout.",[299,43771,43773],{"className":21974,"code":43772,"filename":43727,"language":21976,"meta":307,"style":307},"\u003Cbutton @click=\"logout\">Logout\u003C/button>\n",[179,43774,43775],{"__ignoreMap":307},[1736,43776,43777,43779,43781,43784,43786,43789,43792,43794],{"class":1738,"line":1739},[1736,43778,6657],{"class":1912},[1736,43780,14849],{"class":6696},[1736,43782,43783],{"class":2674}," @click",[1736,43785,5062],{"class":1912},[1736,43787,43788],{"class":1935},"\"logout\"",[1736,43790,43791],{"class":1912},">Logout\u003C/",[1736,43793,14849],{"class":6696},[1736,43795,6663],{"class":1912},[11,43797,43798],{},"Then in this method we set the value of our user in the store to be null and then use the router push to push the route to the index or home route.",[299,43800,43802],{"className":8734,"code":43801,"filename":43727,"language":8736,"meta":307,"style":307},"methods: {\n  logout () {\n    this.$store.commit('auth/setUser', null)\n    this.$store.commit('auth/setPass', null)\n    this.$router.push({name: 'index'})\n    }\n  }\n",[179,43803,43804,43810,43817,43835,43853,43870,43874],{"__ignoreMap":307},[1736,43805,43806,43808],{"class":1738,"line":1739},[1736,43807,35126],{"class":2674},[1736,43809,1922],{"class":1912},[1736,43811,43812,43815],{"class":1738,"line":748},[1736,43813,43814],{"class":2674},"  logout",[1736,43816,43467],{"class":1912},[1736,43818,43819,43821,43823,43825,43827,43829,43831,43833],{"class":1738,"line":756},[1736,43820,15666],{"class":1918},[1736,43822,43529],{"class":1912},[1736,43824,43046],{"class":2674},[1736,43826,7751],{"class":1912},[1736,43828,43536],{"class":1935},[1736,43830,829],{"class":1912},[1736,43832,18634],{"class":1918},[1736,43834,7045],{"class":1912},[1736,43836,43837,43839,43841,43843,43845,43847,43849,43851],{"class":1738,"line":1755},[1736,43838,15666],{"class":1918},[1736,43840,43529],{"class":1912},[1736,43842,43046],{"class":2674},[1736,43844,7751],{"class":1912},[1736,43846,43556],{"class":1935},[1736,43848,829],{"class":1912},[1736,43850,18634],{"class":1918},[1736,43852,7045],{"class":1912},[1736,43854,43855,43857,43860,43862,43865,43868],{"class":1738,"line":1761},[1736,43856,15666],{"class":1918},[1736,43858,43859],{"class":1912},".$router.",[1736,43861,21424],{"class":2674},[1736,43863,43864],{"class":1912},"({name: ",[1736,43866,43867],{"class":1935},"'index'",[1736,43869,10582],{"class":1912},[1736,43871,43872],{"class":1738,"line":1767},[1736,43873,9853],{"class":1912},[1736,43875,43876],{"class":1738,"line":1772},[1736,43877,1971],{"class":1912},[138,43879,43881],{"id":43880},"testing-it-out","Testing it out",[11,43883,43884],{},"Now if we try to access our admin page we are redirected to the login page and once we add a username and password we can login and see our admin page.",[11,43886,43887],{},"We can also see this in the vue dev tools under the vuex options. Our auth of setUser and auth of setPass are called and the user and password are updated.",[11,43889,43890],{},"And if we press logout you will see we are redirected to the home page and the user and password is set to null. Which means our login and logout are working exactly as they should.",[11,43892,43893],{},"Of course this is not a very secure method of adding login as it is not checking if the username or password are correct. This example is just to show you how middleware works.",[731,43895],{},[23,43897,43899],{"id":43898},"anonymous-middleware","Anonymous Middleware",[11,43901,43902],{},"If we need to use Middleware only on a specific page we can directly use a function for it instead of creating a middleware file. Let's say we wanted to have a page where the user could see how many times they had visited it.",[138,43904,43906],{"id":43905},"setting-up-the-store-for-our-anonymous-middleware","Setting up the Store for our Anonymous Middleware",[11,43908,43909],{},"We can set up our store by adding a js file, then Nuxt will know to include the store in the bundle. We can set the state of pageVisits equal to 0 and add a mutation of increment, passing in the state and increasing the page visits by one.",[299,43911,43914],{"className":8734,"code":43912,"filename":43913,"language":8736,"meta":307,"style":307},"export const state = () => ({\n  pageVisits: 0\n})\n\nexport const mutations = {\n  increment(state) {\n    state.pageVisits++\n  }\n}\n","store/analytics.js",[179,43915,43916,43932,43940,43944,43948,43960,43970,43978,43982],{"__ignoreMap":307},[1736,43917,43918,43920,43922,43924,43926,43928,43930],{"class":1738,"line":1739},[1736,43919,6632],{"class":4866},[1736,43921,7002],{"class":4866},[1736,43923,42930],{"class":2674},[1736,43925,4911],{"class":4866},[1736,43927,7010],{"class":1912},[1736,43929,7013],{"class":4866},[1736,43931,41782],{"class":1912},[1736,43933,43934,43937],{"class":1738,"line":748},[1736,43935,43936],{"class":1912},"  pageVisits: ",[1736,43938,43939],{"class":1918},"0\n",[1736,43941,43942],{"class":1738,"line":756},[1736,43943,10582],{"class":1912},[1736,43945,43946],{"class":1738,"line":1755},[1736,43947,1747],{"emptyLinePlaceholder":790},[1736,43949,43950,43952,43954,43956,43958],{"class":1738,"line":1761},[1736,43951,6632],{"class":4866},[1736,43953,7002],{"class":4866},[1736,43955,42963],{"class":1918},[1736,43957,4911],{"class":4866},[1736,43959,4914],{"class":1912},[1736,43961,43962,43964,43966,43968],{"class":1738,"line":1767},[1736,43963,8877],{"class":2674},[1736,43965,7751],{"class":1912},[1736,43967,42977],{"class":5036},[1736,43969,7246],{"class":1912},[1736,43971,43972,43975],{"class":1738,"line":1772},[1736,43973,43974],{"class":1912},"    state.pageVisits",[1736,43976,43977],{"class":4866},"++\n",[1736,43979,43980],{"class":1738,"line":1778},[1736,43981,1971],{"class":1912},[1736,43983,43984],{"class":1738,"line":1784},[1736,43985,1976],{"class":1912},[11,43987,43988],{},"Then on whatever page we want we can show the value of our page visits from the store.",[299,43990,43993],{"className":21974,"code":43991,"filename":43992,"language":21976,"meta":307,"style":307},"\u003Cp>Page visits: {{this.$store.state.analytics.pageVisits}}\u003C/p>\n","pages/analytics.vue",[179,43994,43995],{"__ignoreMap":307},[1736,43996,43997,43999,44001,44004,44006],{"class":1738,"line":1739},[1736,43998,6657],{"class":1912},[1736,44000,11],{"class":6696},[1736,44002,44003],{"class":1912},">Page visits: {{this.$store.state.analytics.pageVisits}}\u003C/",[1736,44005,11],{"class":6696},[1736,44007,6663],{"class":1912},[11,44009,44010],{},"We then need to add our middleware so that before we render the page this number is updated. We can do that by creating a middleware function and passing in the store from the context. Then we commit to the store our increment mutation from our analytics file which will increment our page visits by one.",[299,44012,44014],{"className":8734,"code":44013,"filename":43992,"language":8736,"meta":307,"style":307},"\u003Cscript>\nexport default {\n  middleware ({ store }) {\n    store.commit('analytics/increment')\n  }\n}\n\u003C/script>\n",[179,44015,44016,44024,44028,44036,44046,44050,44054],{"__ignoreMap":307},[1736,44017,44018,44020,44022],{"class":1738,"line":1739},[1736,44019,6657],{"class":1912},[1736,44021,21869],{"class":6696},[1736,44023,6663],{"class":1912},[1736,44025,44026],{"class":1738,"line":748},[1736,44027,21876],{"class":1912},[1736,44029,44030,44033],{"class":1738,"line":756},[1736,44031,44032],{"class":2674},"  middleware",[1736,44034,44035],{"class":1912}," ({ store }) {\n",[1736,44037,44038,44041,44044],{"class":1738,"line":1755},[1736,44039,44040],{"class":1912},"    store.commit(",[1736,44042,44043],{"class":1935},"'analytics/increment'",[1736,44045,7045],{"class":1912},[1736,44047,44048],{"class":1738,"line":1761},[1736,44049,1971],{"class":1912},[1736,44051,44052],{"class":1738,"line":1767},[1736,44053,1976],{"class":1912},[1736,44055,44056],{"class":1738,"line":1772},[1736,44057,44058],{"class":1912},"\u003C/script>\n",[11,44060,44061],{},"In the browser you will see the number increments every time we open this page.",[731,44063],{},[23,44065,5706],{"id":5705},[70,44067,44068,44075,44081,44088,44095],{},[73,44069,44070],{},[15,44071,44074],{"href":44072,"rel":44073},"https://nuxtjs.org/docs/2.x/directory-structure/middleware",[19],"Nuxt Middleware Docs",[73,44076,44077],{},[15,44078,44080],{"href":42878,"rel":44079},[19],"Nuxt Context Docs",[73,44082,44083],{},[15,44084,44087],{"href":44085,"rel":44086},"https://nuxtjs.org/examples/middleware-router",[19],"Router Middleware Example",[73,44089,44090],{},[15,44091,44094],{"href":44092,"rel":44093},"https://nuxtjs.org/examples/middleware-named",[19],"Named Middleware Example",[73,44096,44097],{},[15,44098,44101],{"href":44099,"rel":44100},"https://nuxtjs.org/examples/middleware-anonymous",[19],"Anonymous Middleware Example",[2011,44103,44104],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":44106},[44107,44114,44121,44124],{"id":42889,"depth":748,"text":42890,"children":44108},[44109,44110,44111,44112,44113],{"id":42896,"depth":756,"text":42897},{"id":43005,"depth":756,"text":43006},{"id":43061,"depth":756,"text":43062},{"id":43089,"depth":756,"text":43090},{"id":43139,"depth":756,"text":43140},{"id":43176,"depth":748,"text":43177,"children":44115},[44116,44117,44118,44119,44120],{"id":43269,"depth":756,"text":43270},{"id":43589,"depth":756,"text":43590},{"id":43719,"depth":756,"text":43720},{"id":43765,"depth":756,"text":43766},{"id":43880,"depth":756,"text":43881},{"id":43898,"depth":748,"text":43899,"children":44122},[44123],{"id":43905,"depth":756,"text":43906},{"id":5705,"depth":748,"text":5706},"2021-02-18","Middleware lets you define custom functions that can be run before rendering either a page or a group of pages which we call layouts.","photo-1446769357257-5aa1b1bfcd65?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop",{},"/blog/nuxt-middleware",{"title":42867,"description":44126},"blog/nuxt-middleware",[5239],"BLFLWLXpZhOa49L8J7tvlDM9aZwsLSKJLeizG2pn5W4",{"id":44135,"title":44136,"body":44137,"canonical":788,"date":21827,"description":45081,"extension":786,"featured":787,"image":39926,"meta":45082,"navigation":790,"ogimage":788,"path":45083,"provider":5235,"published":787,"seo":45084,"stem":45085,"tags":45086,"url":788,"__hash__":45087},"blog/blog/nuxt-migrating-to-nuxt-content.md","Migrating to Nuxt content",{"type":8,"value":44138,"toc":45066},[44139,44148,44150,44153,44156,44174,44177,44181,44184,44288,44292,44295,44404,44408,44412,44415,44495,44498,44502,44505,44554,44558,44561,44609,44613,44616,44639,44642,44676,44683,44687,44695,44719,44723,44726,44828,44831,44834,44838,44847,45009,45013,45016,45053,45056,45063],[11,44140,44141,44142,44147],{},"Nuxt content has released a ",[15,44143,44146],{"href":44144,"rel":44145},"https://content.nuxtjs.org/themes/docs",[19],"docs theme"," so that you can easily create and manage documentation for your module or project using markdown files. You also have the possibility of adding vue components into your markdown as well as modifying the color scheme with tailwind and so much more. If you already have documentation set up and would like to migrate to Nuxt Content then this guide should help you make migrate easily and take advantage of what the docs theme gives you.",[23,44149,15435],{"id":15434},[11,44151,44152],{},"If you already have a docs folder make sure you first rename it to old-docs or similar then it will be easier to move all the markdown files into the new docs folder.",[11,44154,44155],{},"In the project run the command",[299,44157,44159],{"className":2665,"code":44158,"language":2667,"meta":307,"style":307},"yarn create nuxt-content-docs docs\n",[179,44160,44161],{"__ignoreMap":307},[1736,44162,44163,44165,44168,44171],{"class":1738,"line":1739},[1736,44164,6575],{"class":2674},[1736,44166,44167],{"class":1935}," create",[1736,44169,44170],{"class":1935}," nuxt-content-docs",[1736,44172,44173],{"class":1935}," docs\n",[11,44175,44176],{},"You can answer all prompts or just press enter and answer them later.",[23,44178,44180],{"id":44179},"modifying-the-settings","Modifying the settings",[11,44182,44183],{},"In the content folder inside the docs folder you can fill in the following info:",[299,44185,44187],{"className":1903,"code":44186,"language":1905,"meta":307,"style":307},"{\n  \"title\": \"Nuxt Content\", // title of your module\n  \"url\": \"https://content.nuxtjs.org\", // url of your docs\n  \"logo\": {\n    \"light\": \"/logo-light.svg\",\n    \"dark\": \"/logo-dark.svg\"\n  },\n  \"github\": \"nuxt/content\", // module name on github\n  \"twitter\": \"@nuxt_js\" // twitter handler\n}\n",[179,44188,44189,44193,44208,44223,44230,44242,44252,44256,44271,44284],{"__ignoreMap":307},[1736,44190,44191],{"class":1738,"line":1739},[1736,44192,1913],{"class":1912},[1736,44194,44195,44198,44200,44203,44205],{"class":1738,"line":748},[1736,44196,44197],{"class":1918},"  \"title\"",[1736,44199,3065],{"class":1912},[1736,44201,44202],{"class":1935},"\"Nuxt Content\"",[1736,44204,829],{"class":1912},[1736,44206,44207],{"class":6820},"// title of your module\n",[1736,44209,44210,44213,44215,44218,44220],{"class":1738,"line":756},[1736,44211,44212],{"class":1918},"  \"url\"",[1736,44214,3065],{"class":1912},[1736,44216,44217],{"class":1935},"\"https://content.nuxtjs.org\"",[1736,44219,829],{"class":1912},[1736,44221,44222],{"class":6820},"// url of your docs\n",[1736,44224,44225,44228],{"class":1738,"line":1755},[1736,44226,44227],{"class":1918},"  \"logo\"",[1736,44229,1922],{"class":1912},[1736,44231,44232,44235,44237,44240],{"class":1738,"line":1761},[1736,44233,44234],{"class":1918},"    \"light\"",[1736,44236,3065],{"class":1912},[1736,44238,44239],{"class":1935},"\"/logo-light.svg\"",[1736,44241,1939],{"class":1912},[1736,44243,44244,44247,44249],{"class":1738,"line":1767},[1736,44245,44246],{"class":1918},"    \"dark\"",[1736,44248,3065],{"class":1912},[1736,44250,44251],{"class":1935},"\"/logo-dark.svg\"\n",[1736,44253,44254],{"class":1738,"line":1772},[1736,44255,4929],{"class":1912},[1736,44257,44258,44261,44263,44266,44268],{"class":1738,"line":1778},[1736,44259,44260],{"class":1918},"  \"github\"",[1736,44262,3065],{"class":1912},[1736,44264,44265],{"class":1935},"\"nuxt/content\"",[1736,44267,829],{"class":1912},[1736,44269,44270],{"class":6820},"// module name on github\n",[1736,44272,44273,44276,44278,44281],{"class":1738,"line":1784},[1736,44274,44275],{"class":1918},"  \"twitter\"",[1736,44277,3065],{"class":1912},[1736,44279,44280],{"class":1935},"\"@nuxt_js\"",[1736,44282,44283],{"class":6820}," // twitter handler\n",[1736,44285,44286],{"class":1738,"line":1790},[1736,44287,1976],{"class":1912},[23,44289,44291],{"id":44290},"adding-your-images","Adding your images",[11,44293,44294],{},"In order for the images to work you will need to add your own logo and images to the static folder keeping the same naming convention:",[299,44296,44298],{"className":2665,"code":44297,"language":2667,"meta":307,"style":307},"static/\n  icon.png // for pwa\n  logo - dark.svg // dark logo\n  logo - light.svg // light logo\n  preview.png // main preview image for main page and social sharing\n  preview - dark.png // dark mode preview image\n",[179,44299,44300,44305,44318,44336,44352,44382],{"__ignoreMap":307},[1736,44301,44302],{"class":1738,"line":1739},[1736,44303,44304],{"class":2674},"static/\n",[1736,44306,44307,44310,44313,44315],{"class":1738,"line":748},[1736,44308,44309],{"class":2674},"  icon.png",[1736,44311,44312],{"class":1935}," //",[1736,44314,43313],{"class":1935},[1736,44316,44317],{"class":1935}," pwa\n",[1736,44319,44320,44323,44325,44328,44330,44333],{"class":1738,"line":756},[1736,44321,44322],{"class":2674},"  logo",[1736,44324,28581],{"class":1935},[1736,44326,44327],{"class":1935}," dark.svg",[1736,44329,44312],{"class":1935},[1736,44331,44332],{"class":1935}," dark",[1736,44334,44335],{"class":1935}," logo\n",[1736,44337,44338,44340,44342,44345,44347,44350],{"class":1738,"line":1755},[1736,44339,44322],{"class":2674},[1736,44341,28581],{"class":1935},[1736,44343,44344],{"class":1935}," light.svg",[1736,44346,44312],{"class":1935},[1736,44348,44349],{"class":1935}," light",[1736,44351,44335],{"class":1935},[1736,44353,44354,44357,44359,44362,44365,44367,44369,44371,44374,44376,44379],{"class":1738,"line":1761},[1736,44355,44356],{"class":2674},"  preview.png",[1736,44358,44312],{"class":1935},[1736,44360,44361],{"class":1935}," main",[1736,44363,44364],{"class":1935}," preview",[1736,44366,30796],{"class":1935},[1736,44368,43313],{"class":1935},[1736,44370,44361],{"class":1935},[1736,44372,44373],{"class":1935}," page",[1736,44375,25030],{"class":1935},[1736,44377,44378],{"class":1935}," social",[1736,44380,44381],{"class":1935}," sharing\n",[1736,44383,44384,44387,44389,44392,44394,44396,44399,44401],{"class":1738,"line":1767},[1736,44385,44386],{"class":2674},"  preview",[1736,44388,28581],{"class":1935},[1736,44390,44391],{"class":1935}," dark.png",[1736,44393,44312],{"class":1935},[1736,44395,44332],{"class":1935},[1736,44397,44398],{"class":1935}," mode",[1736,44400,44364],{"class":1935},[1736,44402,44403],{"class":1935}," image\n",[23,44405,44407],{"id":44406},"modifying-the-content","Modifying the content",[138,44409,44411],{"id":44410},"index-page","index page",[11,44413,44414],{},"The index page contains an empty category with position 1. This means it will be at the top of the navigation and not be under any category making it easier to find. The features are what will be shown in the list and more features can be added.",[299,44416,44419],{"className":44417,"code":44418,"language":33587,"meta":307,"style":307},"language-yaml shiki shiki-themes github-light github-dark","---\ntitle: Introduction\ndescription: i18n (Internationalization) for your Nuxt project\nposition: 1\ncategory: ''\nfeatures:\n  - Integration with vue-i18n\n  - Automatic routes generation and custom paths\n  - Search Engine Optimization\n---\n",[179,44420,44421,44425,44434,44443,44453,44462,44469,44477,44484,44491],{"__ignoreMap":307},[1736,44422,44423],{"class":1738,"line":1739},[1736,44424,32078],{"class":2674},[1736,44426,44427,44429,44431],{"class":1738,"line":748},[1736,44428,7176],{"class":6696},[1736,44430,3065],{"class":1912},[1736,44432,44433],{"class":1935},"Introduction\n",[1736,44435,44436,44438,44440],{"class":1738,"line":756},[1736,44437,2432],{"class":6696},[1736,44439,3065],{"class":1912},[1736,44441,44442],{"class":1935},"i18n (Internationalization) for your Nuxt project\n",[1736,44444,44445,44448,44450],{"class":1738,"line":1755},[1736,44446,44447],{"class":6696},"position",[1736,44449,3065],{"class":1912},[1736,44451,44452],{"class":1918},"1\n",[1736,44454,44455,44458,44460],{"class":1738,"line":1761},[1736,44456,44457],{"class":6696},"category",[1736,44459,3065],{"class":1912},[1736,44461,42946],{"class":1935},[1736,44463,44464,44467],{"class":1738,"line":1767},[1736,44465,44466],{"class":6696},"features",[1736,44468,15995],{"class":1912},[1736,44470,44471,44474],{"class":1738,"line":1772},[1736,44472,44473],{"class":1912},"  - ",[1736,44475,44476],{"class":1935},"Integration with vue-i18n\n",[1736,44478,44479,44481],{"class":1738,"line":1778},[1736,44480,44473],{"class":1912},[1736,44482,44483],{"class":1935},"Automatic routes generation and custom paths\n",[1736,44485,44486,44488],{"class":1738,"line":1784},[1736,44487,44473],{"class":1912},[1736,44489,44490],{"class":1935},"Search Engine Optimization\n",[1736,44492,44493],{"class":1738,"line":1790},[1736,44494,32078],{"class":2674},[11,44496,44497],{},"Feel free to add more content and of course a video would be great if possible.",[138,44499,44501],{"id":44500},"setup-page","setup page",[11,44503,44504],{},"In the setup page you will see we have a title and description as well as a position of 2 and a category of Guide. This means this will be positioned in the navigation in second place, after the index page and it will be under a heading of Guide.",[299,44506,44508],{"className":44417,"code":44507,"language":33587,"meta":307,"style":307},"---\ntitle: Setup\ndescription: How to setup i18n\nposition: 2\ncategory: Guide\n---\n",[179,44509,44510,44514,44523,44532,44541,44550],{"__ignoreMap":307},[1736,44511,44512],{"class":1738,"line":1739},[1736,44513,32078],{"class":2674},[1736,44515,44516,44518,44520],{"class":1738,"line":748},[1736,44517,7176],{"class":6696},[1736,44519,3065],{"class":1912},[1736,44521,44522],{"class":1935},"Setup\n",[1736,44524,44525,44527,44529],{"class":1738,"line":756},[1736,44526,2432],{"class":6696},[1736,44528,3065],{"class":1912},[1736,44530,44531],{"class":1935},"How to setup i18n\n",[1736,44533,44534,44536,44538],{"class":1738,"line":1755},[1736,44535,44447],{"class":6696},[1736,44537,3065],{"class":1912},[1736,44539,44540],{"class":1918},"2\n",[1736,44542,44543,44545,44547],{"class":1738,"line":1761},[1736,44544,44457],{"class":6696},[1736,44546,3065],{"class":1912},[1736,44548,44549],{"class":1935},"Guide\n",[1736,44551,44552],{"class":1738,"line":1767},[1736,44553,32078],{"class":2674},[138,44555,44557],{"id":44556},"adding-more-pages","Adding more pages",[11,44559,44560],{},"All pages from the old docs can be copied over to this new docs folder inside the en folder for english docs. Then every page will need the yaml block at the top modified to include the title and description and the category and position. To create a new category just add the new category title such as API instead of Guide for example and then the position number. Do not use position 1 here as the position does not start at the beginning when creating a new category. You can use position 10 or 100 for each category if you find it keeps things easier to manage.",[299,44562,44564],{"className":44417,"code":44563,"language":33587,"meta":307,"style":307},"---\ntitle: API Reference\ndescription: API Reference\nposition: 20\ncategory: Api\n---\n",[179,44565,44566,44570,44579,44587,44596,44605],{"__ignoreMap":307},[1736,44567,44568],{"class":1738,"line":1739},[1736,44569,32078],{"class":2674},[1736,44571,44572,44574,44576],{"class":1738,"line":748},[1736,44573,7176],{"class":6696},[1736,44575,3065],{"class":1912},[1736,44577,44578],{"class":1935},"API Reference\n",[1736,44580,44581,44583,44585],{"class":1738,"line":756},[1736,44582,2432],{"class":6696},[1736,44584,3065],{"class":1912},[1736,44586,44578],{"class":1935},[1736,44588,44589,44591,44593],{"class":1738,"line":1755},[1736,44590,44447],{"class":6696},[1736,44592,3065],{"class":1912},[1736,44594,44595],{"class":1918},"20\n",[1736,44597,44598,44600,44602],{"class":1738,"line":1761},[1736,44599,44457],{"class":6696},[1736,44601,3065],{"class":1912},[1736,44603,44604],{"class":1935},"Api\n",[1736,44606,44607],{"class":1738,"line":1767},[1736,44608,32078],{"class":2674},[23,44610,44612],{"id":44611},"making-use-of-the-components","Making use of the components",[11,44614,44615],{},"You may have in your previous docs used notes or alerts. These can now be modified to use the alert component.",[70,44617,44618,44624,44629,44634],{},[73,44619,44620,44623],{},[179,44621,44622],{},"\u003Calert type=\"info\">\u003C/alert>"," (info is the default value)",[73,44625,44626],{},[179,44627,44628],{},"\u003Calert type=\"warning\">\u003C/alert>",[73,44630,44631],{},[179,44632,44633],{},"\u003Calert type=\"success\">\u003C/alert>",[73,44635,44636],{},[179,44637,44638],{},"\u003Calert type=\"danger\">\u003C/alert>",[11,44640,44641],{},"If you are adding code blocks inside these alerts you may find that it doesn't work properly. This is because components inside markdown require a space between the html and the markdown content when it has a link inside it or highlighting. A best practice is to always leave an empty line between them.",[299,44643,44645],{"className":21974,"code":44644,"language":21976,"meta":307,"style":307},"\u003Calert type=\"warning\">\n  This is a `warning` with an empty line above and below\n\u003C/alert>\n",[179,44646,44647,44663,44668],{"__ignoreMap":307},[1736,44648,44649,44651,44654,44656,44658,44661],{"class":1738,"line":1739},[1736,44650,6657],{"class":1912},[1736,44652,44653],{"class":30490},"alert",[1736,44655,6635],{"class":2674},[1736,44657,5062],{"class":1912},[1736,44659,44660],{"class":1935},"\"warning\"",[1736,44662,6663],{"class":1912},[1736,44664,44665],{"class":1738,"line":748},[1736,44666,44667],{"class":1912},"  This is a `warning` with an empty line above and below\n",[1736,44669,44670,44672,44674],{"class":1738,"line":756},[1736,44671,8105],{"class":1912},[1736,44673,44653],{"class":30490},[1736,44675,6663],{"class":1912},[11,44677,37016,44678,44682],{},[15,44679,41523],{"href":44680,"rel":44681},"https://content.nuxtjs.org/themes/docs#components",[19]," for more available components",[23,44684,44686],{"id":44685},"adding-file-names","Adding file Names",[11,44688,44689,44690,44694],{},"You may already have some code in your markdown with the filename as a comment. This can be improved as the docs theme will show the filename in the top corner of the code which looks much tidier. You can do this by adding the filename inside square brackets after the type of code you want to show. The curly brackets are used for code highlighting. See the ",[15,44691,41523],{"href":44692,"rel":44693},"https://content.nuxtjs.org/writing#codeblocks",[19]," for more info regarding code highlighting.",[299,44696,44698],{"className":8734,"code":44697,"filename":33252,"language":8736,"meta":307,"style":307},"{\n  modules: ['nuxt-i18n'],\n}\n\n",[179,44699,44700,44704,44715],{"__ignoreMap":307},[1736,44701,44702],{"class":1738,"line":1739},[1736,44703,1913],{"class":1912},[1736,44705,44706,44708,44710,44713],{"class":1738,"line":748},[1736,44707,37364],{"class":2674},[1736,44709,16439],{"class":1912},[1736,44711,44712],{"class":1935},"'nuxt-i18n'",[1736,44714,16460],{"class":1912},[1736,44716,44717],{"class":1738,"line":756},[1736,44718,1976],{"class":1912},[23,44720,44722],{"id":44721},"adding-another-language","Adding another language",[11,44724,44725],{},"If you documentation is in multiple languages then there is very little you have to do in order for it to work. First add the language to the nuxt config file inside the default theme.",[299,44727,44729],{"className":8734,"code":44728,"language":8736,"meta":307,"style":307},"export default theme({\n  i18n: {\n    locales: () => [\n      { code: 'fr', iso: 'fr-FR', file: 'fr-FR.js', name: 'Français' },\n      { code: 'en', iso: 'en-US', file: 'en-US.js', name: 'English' }\n    ],\n    defaultLocale: 'en'\n  }\n})\n",[179,44730,44731,44742,44747,44758,44786,44808,44812,44820,44824],{"__ignoreMap":307},[1736,44732,44733,44735,44737,44740],{"class":1738,"line":1739},[1736,44734,6632],{"class":4866},[1736,44736,30438],{"class":4866},[1736,44738,44739],{"class":2674}," theme",[1736,44741,5122],{"class":1912},[1736,44743,44744],{"class":1738,"line":748},[1736,44745,44746],{"class":1912},"  i18n: {\n",[1736,44748,44749,44752,44754,44756],{"class":1738,"line":756},[1736,44750,44751],{"class":2674},"    locales",[1736,44753,13948],{"class":1912},[1736,44755,7013],{"class":4866},[1736,44757,28906],{"class":1912},[1736,44759,44760,44763,44766,44769,44772,44775,44778,44781,44784],{"class":1738,"line":1755},[1736,44761,44762],{"class":1912},"      { code: ",[1736,44764,44765],{"class":1935},"'fr'",[1736,44767,44768],{"class":1912},", iso: ",[1736,44770,44771],{"class":1935},"'fr-FR'",[1736,44773,44774],{"class":1912},", file: ",[1736,44776,44777],{"class":1935},"'fr-FR.js'",[1736,44779,44780],{"class":1912},", name: ",[1736,44782,44783],{"class":1935},"'Français'",[1736,44785,7197],{"class":1912},[1736,44787,44788,44790,44792,44794,44796,44798,44801,44803,44806],{"class":1738,"line":1761},[1736,44789,44762],{"class":1912},[1736,44791,21941],{"class":1935},[1736,44793,44768],{"class":1912},[1736,44795,22206],{"class":1935},[1736,44797,44774],{"class":1912},[1736,44799,44800],{"class":1935},"'en-US.js'",[1736,44802,44780],{"class":1912},[1736,44804,44805],{"class":1935},"'English'",[1736,44807,21922],{"class":1912},[1736,44809,44810],{"class":1738,"line":1767},[1736,44811,1949],{"class":1912},[1736,44813,44814,44817],{"class":1738,"line":1772},[1736,44815,44816],{"class":1912},"    defaultLocale: ",[1736,44818,44819],{"class":1935},"'en'\n",[1736,44821,44822],{"class":1738,"line":1778},[1736,44823,1971],{"class":1912},[1736,44825,44826],{"class":1738,"line":1784},[1736,44827,10582],{"class":1912},[11,44829,44830],{},"Then create the folder for the new language for example fr for french. And then add your translated docs here.",[11,44832,44833],{},"You will now see at the bottom that there is an icon for languages and clicking this will change the page to the new language.",[23,44835,44837],{"id":44836},"changing-the-color-scheme","Changing the color scheme",[11,44839,44840,44841,44846],{},"If you want to add your own colors you can override the default tailwind color scheme. Check out this awesome ",[15,44842,44845],{"href":44843,"rel":44844},"https://javisperez.github.io/tailwindcolorshades/#/",[19],"color shades generator for TailwindCSS"," so you can create these colours easily.",[299,44848,44850],{"className":8734,"code":44849,"language":8736,"meta":307,"style":307},"module.exports = {\n  theme: {\n    extend: {\n      colors: {\n        primary: {\n          100: '#FCEDEE',\n          200: '#F8D3D5',\n          300: '#F3B9BB',\n          400: '#EB8488',\n          500: '#E24F55',\n          600: '#CB474D',\n          700: '#882F33',\n          800: '#662426',\n          900: '#44181A'\n        }\n      }\n    }\n  }\n}\n",[179,44851,44852,44864,44868,44873,44878,44883,44895,44907,44919,44931,44943,44955,44967,44979,44989,44993,44997,45001,45005],{"__ignoreMap":307},[1736,44853,44854,44856,44858,44860,44862],{"class":1738,"line":1739},[1736,44855,4903],{"class":1918},[1736,44857,891],{"class":1912},[1736,44859,4908],{"class":1918},[1736,44861,4911],{"class":4866},[1736,44863,4914],{"class":1912},[1736,44865,44866],{"class":1738,"line":748},[1736,44867,4919],{"class":1912},[1736,44869,44870],{"class":1738,"line":756},[1736,44871,44872],{"class":1912},"    extend: {\n",[1736,44874,44875],{"class":1738,"line":1755},[1736,44876,44877],{"class":1912},"      colors: {\n",[1736,44879,44880],{"class":1738,"line":1761},[1736,44881,44882],{"class":1912},"        primary: {\n",[1736,44884,44885,44888,44890,44893],{"class":1738,"line":1767},[1736,44886,44887],{"class":1918},"          100",[1736,44889,3065],{"class":1912},[1736,44891,44892],{"class":1935},"'#FCEDEE'",[1736,44894,1939],{"class":1912},[1736,44896,44897,44900,44902,44905],{"class":1738,"line":1772},[1736,44898,44899],{"class":1918},"          200",[1736,44901,3065],{"class":1912},[1736,44903,44904],{"class":1935},"'#F8D3D5'",[1736,44906,1939],{"class":1912},[1736,44908,44909,44912,44914,44917],{"class":1738,"line":1778},[1736,44910,44911],{"class":1918},"          300",[1736,44913,3065],{"class":1912},[1736,44915,44916],{"class":1935},"'#F3B9BB'",[1736,44918,1939],{"class":1912},[1736,44920,44921,44924,44926,44929],{"class":1738,"line":1784},[1736,44922,44923],{"class":1918},"          400",[1736,44925,3065],{"class":1912},[1736,44927,44928],{"class":1935},"'#EB8488'",[1736,44930,1939],{"class":1912},[1736,44932,44933,44936,44938,44941],{"class":1738,"line":1790},[1736,44934,44935],{"class":1918},"          500",[1736,44937,3065],{"class":1912},[1736,44939,44940],{"class":1935},"'#E24F55'",[1736,44942,1939],{"class":1912},[1736,44944,44945,44948,44950,44953],{"class":1738,"line":1796},[1736,44946,44947],{"class":1918},"          600",[1736,44949,3065],{"class":1912},[1736,44951,44952],{"class":1935},"'#CB474D'",[1736,44954,1939],{"class":1912},[1736,44956,44957,44960,44962,44965],{"class":1738,"line":2353},[1736,44958,44959],{"class":1918},"          700",[1736,44961,3065],{"class":1912},[1736,44963,44964],{"class":1935},"'#882F33'",[1736,44966,1939],{"class":1912},[1736,44968,44969,44972,44974,44977],{"class":1738,"line":2358},[1736,44970,44971],{"class":1918},"          800",[1736,44973,3065],{"class":1912},[1736,44975,44976],{"class":1935},"'#662426'",[1736,44978,1939],{"class":1912},[1736,44980,44981,44984,44986],{"class":1738,"line":2364},[1736,44982,44983],{"class":1918},"          900",[1736,44985,3065],{"class":1912},[1736,44987,44988],{"class":1935},"'#44181A'\n",[1736,44990,44991],{"class":1738,"line":2370},[1736,44992,26121],{"class":1912},[1736,44994,44995],{"class":1738,"line":2376},[1736,44996,14448],{"class":1912},[1736,44998,44999],{"class":1738,"line":2381},[1736,45000,9853],{"class":1912},[1736,45002,45003],{"class":1738,"line":2387},[1736,45004,1971],{"class":1912},[1736,45006,45007],{"class":1738,"line":2393},[1736,45008,1976],{"class":1912},[23,45010,45012],{"id":45011},"modifying-or-adding-the-build-command","Modifying or adding the Build command",[11,45014,45015],{},"Add a netlify.toml or modify the existing one if your docs are being published to Netlify. We need to use the base of docs, if that is what you named the folder for your docs. Then add the generate command to generate a static site and the dist folder so Netlify knows which folder to publish and the ignore rule to make sure package.json is kept up to date as Netlify will skip the build process if only the content has changed.",[299,45017,45019],{"className":21726,"code":45018,"language":21728,"meta":307,"style":307},"# https://docs.netlify.com/configure-builds/file-based-configuration\n\n[build]\n  base    = \"docs\"\n  command = \"yarn generate\"\n  publish = \"dist\"\n  ignore  = \"git diff --quiet HEAD^ HEAD . ../package.json\"\n",[179,45020,45021,45026,45030,45034,45039,45044,45048],{"__ignoreMap":307},[1736,45022,45023],{"class":1738,"line":1739},[1736,45024,45025],{},"# https://docs.netlify.com/configure-builds/file-based-configuration\n",[1736,45027,45028],{"class":1738,"line":748},[1736,45029,1747],{"emptyLinePlaceholder":790},[1736,45031,45032],{"class":1738,"line":756},[1736,45033,21735],{},[1736,45035,45036],{"class":1738,"line":1755},[1736,45037,45038],{},"  base    = \"docs\"\n",[1736,45040,45041],{"class":1738,"line":1761},[1736,45042,45043],{},"  command = \"yarn generate\"\n",[1736,45045,45046],{"class":1738,"line":1767},[1736,45047,21745],{},[1736,45049,45050],{"class":1738,"line":1772},[1736,45051,45052],{},"  ignore  = \"git diff --quiet HEAD^ HEAD . ../package.json\"\n",[11,45054,45055],{},"And that's it your new docs theme is now ready to go live. Don't forget to remove any previous unused packages and delete the old docs folder that you no longer need.",[11,45057,45058,45059,45062],{},"There are more things you can add such as a codeSandbox or your own custom components. Check out the ",[15,45060,41523],{"href":44144,"rel":45061},[19]," for more things that can be done with the nuxt content docs theme.",[2011,45064,45065],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":307,"searchDepth":748,"depth":748,"links":45067},[45068,45069,45070,45071,45076,45077,45078,45079,45080],{"id":15434,"depth":748,"text":15435},{"id":44179,"depth":748,"text":44180},{"id":44290,"depth":748,"text":44291},{"id":44406,"depth":748,"text":44407,"children":45072},[45073,45074,45075],{"id":44410,"depth":756,"text":44411},{"id":44500,"depth":756,"text":44501},{"id":44556,"depth":756,"text":44557},{"id":44611,"depth":748,"text":44612},{"id":44685,"depth":748,"text":44686},{"id":44721,"depth":748,"text":44722},{"id":44836,"depth":748,"text":44837},{"id":45011,"depth":748,"text":45012},"If you already have documentation set up and would like to migrate to Nuxt Content then this guide should help you make migrate easily and take advantage of what the docs theme gives you.",{},"/blog/nuxt-migrating-to-nuxt-content",{"title":44136,"description":45081},"blog/nuxt-migrating-to-nuxt-content",[5239],"2o4VKxlDeXaA4Zt1IHaCnNpHTbMlaoZPRPQsPm13Xe4",{"id":45089,"title":45090,"body":45091,"canonical":788,"date":45467,"description":45095,"extension":786,"featured":787,"image":45468,"meta":45469,"navigation":790,"ogimage":788,"path":45470,"provider":5235,"published":787,"seo":45471,"stem":45472,"tags":45473,"url":788,"__hash__":45474},"blog/blog/nuxt-page-and-layout-transitions.md","Page and Layout Transitions in Nuxt.js",{"type":8,"value":45092,"toc":45465},[45093,45096,45105,45173,45178,45204,45210,45272,45278,45303,45306,45368,45375,45400,45410,45423,45459,45462],[11,45094,45095],{},"With Nuxt.js it is really easy to add transitions between your pages. You can create transitions for all your pages or layouts and you can even have different transitions for specific pages.",[11,45097,45098,45099,45101,45102,45104],{},"The default Nuxt.js transition name for pages is ",[179,45100,25307],{},". That means in order to add a fade transition to every page we just need to add the the word ",[179,45103,25307],{}," before our enter and leave transition classes that Vue.js gives us.",[299,45106,45108],{"className":4857,"code":45107,"language":4859,"meta":307,"style":307},".page-enter-active,\n.page-leave-active {\n  transition: opacity 0.5s;\n}\n.page-enter,\n.page-leave-to {\n  opacity: 0;\n}\n",[179,45109,45110,45117,45124,45140,45144,45151,45158,45169],{"__ignoreMap":307},[1736,45111,45112,45115],{"class":1738,"line":1739},[1736,45113,45114],{"class":2674},".page-enter-active",[1736,45116,1939],{"class":1912},[1736,45118,45119,45122],{"class":1738,"line":748},[1736,45120,45121],{"class":2674},".page-leave-active",[1736,45123,4914],{"class":1912},[1736,45125,45126,45129,45132,45135,45138],{"class":1738,"line":756},[1736,45127,45128],{"class":1918},"  transition",[1736,45130,45131],{"class":1912},": opacity ",[1736,45133,45134],{"class":1918},"0.5",[1736,45136,45137],{"class":4866},"s",[1736,45139,7682],{"class":1912},[1736,45141,45142],{"class":1738,"line":1755},[1736,45143,1976],{"class":1912},[1736,45145,45146,45149],{"class":1738,"line":1761},[1736,45147,45148],{"class":2674},".page-enter",[1736,45150,1939],{"class":1912},[1736,45152,45153,45156],{"class":1738,"line":1767},[1736,45154,45155],{"class":2674},".page-leave-to",[1736,45157,4914],{"class":1912},[1736,45159,45160,45163,45165,45167],{"class":1738,"line":1772},[1736,45161,45162],{"class":1918},"  opacity",[1736,45164,3065],{"class":1912},[1736,45166,1290],{"class":1918},[1736,45168,7682],{"class":1912},[1736,45170,45171],{"class":1738,"line":1778},[1736,45172,1976],{"class":1912},[11,45174,45175,45176,2900],{},"You can add this css to a global css file and then import it using the ",[179,45177,33252],{},[299,45179,45181],{"className":5635,"code":45180,"language":5637,"meta":307,"style":307},"export default {\n  css: ['~/assets/main.css']\n}\n",[179,45182,45183,45191,45200],{"__ignoreMap":307},[1736,45184,45185,45187,45189],{"class":1738,"line":1739},[1736,45186,6632],{"class":4866},[1736,45188,30438],{"class":4866},[1736,45190,4914],{"class":1912},[1736,45192,45193,45195,45198],{"class":1738,"line":748},[1736,45194,41179],{"class":1912},[1736,45196,45197],{"class":1935},"'~/assets/main.css'",[1736,45199,8420],{"class":1912},[1736,45201,45202],{"class":1738,"line":756},[1736,45203,1976],{"class":1912},[11,45205,45206,45207,891],{},"Layouts work pretty much the same except that instead of writing a class starting with the word page you use the word ",[179,45208,45209],{},"layout",[299,45211,45213],{"className":4857,"code":45212,"language":4859,"meta":307,"style":307},".layout-enter-active,\n.layout-leave-active {\n  transition: opacity 0.5s;\n}\n.layout-enter,\n.layout-leave-active {\n  opacity: 0;\n}\n",[179,45214,45215,45222,45229,45241,45245,45252,45258,45268],{"__ignoreMap":307},[1736,45216,45217,45220],{"class":1738,"line":1739},[1736,45218,45219],{"class":2674},".layout-enter-active",[1736,45221,1939],{"class":1912},[1736,45223,45224,45227],{"class":1738,"line":748},[1736,45225,45226],{"class":2674},".layout-leave-active",[1736,45228,4914],{"class":1912},[1736,45230,45231,45233,45235,45237,45239],{"class":1738,"line":756},[1736,45232,45128],{"class":1918},[1736,45234,45131],{"class":1912},[1736,45236,45134],{"class":1918},[1736,45238,45137],{"class":4866},[1736,45240,7682],{"class":1912},[1736,45242,45243],{"class":1738,"line":1755},[1736,45244,1976],{"class":1912},[1736,45246,45247,45250],{"class":1738,"line":1761},[1736,45248,45249],{"class":2674},".layout-enter",[1736,45251,1939],{"class":1912},[1736,45253,45254,45256],{"class":1738,"line":1767},[1736,45255,45226],{"class":2674},[1736,45257,4914],{"class":1912},[1736,45259,45260,45262,45264,45266],{"class":1738,"line":1772},[1736,45261,45162],{"class":1918},[1736,45263,3065],{"class":1912},[1736,45265,1290],{"class":1918},[1736,45267,7682],{"class":1912},[1736,45269,45270],{"class":1738,"line":1778},[1736,45271,1976],{"class":1912},[11,45273,45274,45275,45277],{},"You can of course create a transition for a specific page such as the home page for example. In your ",[179,45276,33894],{}," file you can add a property of transition and give it the name you like.",[299,45279,45281],{"className":5635,"code":45280,"language":5637,"meta":307,"style":307},"export default {\n  transition: 'home'\n}\n",[179,45282,45283,45291,45299],{"__ignoreMap":307},[1736,45284,45285,45287,45289],{"class":1738,"line":1739},[1736,45286,6632],{"class":4866},[1736,45288,30438],{"class":4866},[1736,45290,4914],{"class":1912},[1736,45292,45293,45296],{"class":1738,"line":748},[1736,45294,45295],{"class":1912},"  transition: ",[1736,45297,45298],{"class":1935},"'home'\n",[1736,45300,45301],{"class":1738,"line":756},[1736,45302,1976],{"class":1912},[11,45304,45305],{},"This name will then be what you use instead of the word page when defining your transition classes.",[299,45307,45309],{"className":4857,"code":45308,"language":4859,"meta":307,"style":307},".home-enter-active,\n.home-leave-active {\n  transition: opacity 0.5s;\n}\n.home-enter,\n.home-leave-active {\n  opacity: 0;\n}\n",[179,45310,45311,45318,45325,45337,45341,45348,45354,45364],{"__ignoreMap":307},[1736,45312,45313,45316],{"class":1738,"line":1739},[1736,45314,45315],{"class":2674},".home-enter-active",[1736,45317,1939],{"class":1912},[1736,45319,45320,45323],{"class":1738,"line":748},[1736,45321,45322],{"class":2674},".home-leave-active",[1736,45324,4914],{"class":1912},[1736,45326,45327,45329,45331,45333,45335],{"class":1738,"line":756},[1736,45328,45128],{"class":1918},[1736,45330,45131],{"class":1912},[1736,45332,45134],{"class":1918},[1736,45334,45137],{"class":4866},[1736,45336,7682],{"class":1912},[1736,45338,45339],{"class":1738,"line":1755},[1736,45340,1976],{"class":1912},[1736,45342,45343,45346],{"class":1738,"line":1761},[1736,45344,45345],{"class":2674},".home-enter",[1736,45347,1939],{"class":1912},[1736,45349,45350,45352],{"class":1738,"line":1767},[1736,45351,45322],{"class":2674},[1736,45353,4914],{"class":1912},[1736,45355,45356,45358,45360,45362],{"class":1738,"line":1772},[1736,45357,45162],{"class":1918},[1736,45359,3065],{"class":1912},[1736,45361,1290],{"class":1918},[1736,45363,7682],{"class":1912},[1736,45365,45366],{"class":1738,"line":1778},[1736,45367,1976],{"class":1912},[11,45369,45370,45371,45374],{},"Nuxt.js wraps your page in a transition component and adds the name to it. In this case the name it adds is ",[179,45372,45373],{},"home",". Basically Nuxt.js does this for you so you don't need to.",[299,45376,45378],{"className":21974,"code":45377,"language":21976,"meta":307,"style":307},"\u003Ctransition name=\"home\">\u003C/transition>\n",[179,45379,45380],{"__ignoreMap":307},[1736,45381,45382,45384,45387,45389,45391,45394,45396,45398],{"class":1738,"line":1739},[1736,45383,6657],{"class":1912},[1736,45385,45386],{"class":30490},"transition",[1736,45388,30516],{"class":2674},[1736,45390,5062],{"class":1912},[1736,45392,45393],{"class":1935},"\"home\"",[1736,45395,8252],{"class":1912},[1736,45397,45386],{"class":30490},[1736,45399,6663],{"class":1912},[11,45401,45402,45403,45406,45407,45409],{},"There is a slight difference in the default mode in Nuxt.js compared to Vue.js. The default mode in Nuxt.js is ",[179,45404,45405],{},"out-in",". Should you wish to change this you can do so using the ",[179,45408,32095],{}," key.",[11,45411,45412,45413,36914,45415,45418,45419,45422],{},"However if you want to change the mode across all pages or layouts then you can do so in the ",[179,45414,33252],{},[179,45416,45417],{},"pageTransition"," key for pages and the ",[179,45420,45421],{},"layoutTransition"," key for layouts.",[299,45424,45426],{"className":5635,"code":45425,"language":5637,"meta":307,"style":307},"pageTransition: {\n    name: 'my-page',\n    mode: 'out-in'\n}\n",[179,45427,45428,45434,45445,45455],{"__ignoreMap":307},[1736,45429,45430,45432],{"class":1738,"line":1739},[1736,45431,45417],{"class":2674},[1736,45433,1922],{"class":1912},[1736,45435,45436,45438,45440,45443],{"class":1738,"line":748},[1736,45437,37479],{"class":2674},[1736,45439,3065],{"class":1912},[1736,45441,45442],{"class":1935},"'my-page'",[1736,45444,1939],{"class":1912},[1736,45446,45447,45450,45452],{"class":1738,"line":756},[1736,45448,45449],{"class":2674},"    mode",[1736,45451,3065],{"class":1912},[1736,45453,45454],{"class":1935},"'out-in'\n",[1736,45456,45457],{"class":1738,"line":1755},[1736,45458,1976],{"class":1912},[11,45460,45461],{},"As you can see adding page transitions in Nuxt.js is really easy so why not give it a try!",[2011,45463,45464],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":45466},[],"2020-04-13","photo-1545986467-13cfe33c156e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",{},"/blog/nuxt-page-and-layout-transitions",{"title":45090,"description":45095},"blog/nuxt-page-and-layout-transitions",[5239],"K6OOYQWY0DJt_r9mQRAg9m2buRMZoR6zFPp4RB_DVno",{"id":45476,"title":45477,"body":45478,"canonical":788,"date":45655,"description":45656,"extension":786,"featured":787,"image":45657,"meta":45658,"navigation":790,"ogimage":788,"path":45659,"provider":5235,"published":787,"seo":45660,"stem":45661,"tags":45662,"url":788,"__hash__":45663},"blog/blog/nuxt-show-message-when-offline.md","Show a message when your user is offline with $nuxt.isOffline",{"type":8,"value":45479,"toc":45653},[45480,45487,45494,45498,45563,45566,45569,45572,45579,45644,45647,45650],[11,45481,45482,45483,45486],{},"Did you know that ",[179,45484,45485],{},"$nuxt.isOffline"," can be used to show your users content based on if they are online or not.",[11,45488,45489,45490,45493],{},"By using a ",[179,45491,45492],{},"v-if"," we can show content based on if a user is offline or online.",[11,45495,39172,45496],{},[179,45497,43101],{},[299,45499,45501],{"className":21974,"code":45500,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cdiv v-if=\"$nuxt.isOffline\">You are offline\u003C/div>\n    \u003Cnuxt />\n  \u003C/div>\n\u003C/template>\n",[179,45502,45503,45511,45519,45539,45547,45555],{"__ignoreMap":307},[1736,45504,45505,45507,45509],{"class":1738,"line":1739},[1736,45506,6657],{"class":1912},[1736,45508,21985],{"class":6696},[1736,45510,6663],{"class":1912},[1736,45512,45513,45515,45517],{"class":1738,"line":748},[1736,45514,7020],{"class":1912},[1736,45516,6697],{"class":6696},[1736,45518,6663],{"class":1912},[1736,45520,45521,45523,45525,45527,45529,45532,45535,45537],{"class":1738,"line":756},[1736,45522,6693],{"class":1912},[1736,45524,6697],{"class":6696},[1736,45526,39261],{"class":2674},[1736,45528,5062],{"class":1912},[1736,45530,45531],{"class":1935},"\"$nuxt.isOffline\"",[1736,45533,45534],{"class":1912},">You are offline\u003C/",[1736,45536,6697],{"class":6696},[1736,45538,6663],{"class":1912},[1736,45540,45541,45543,45545],{"class":1738,"line":1755},[1736,45542,6693],{"class":1912},[1736,45544,5239],{"class":30490},[1736,45546,6739],{"class":1912},[1736,45548,45549,45551,45553],{"class":1738,"line":1761},[1736,45550,8096],{"class":1912},[1736,45552,6697],{"class":6696},[1736,45554,6663],{"class":1912},[1736,45556,45557,45559,45561],{"class":1738,"line":1767},[1736,45558,8105],{"class":1912},[1736,45560,21985],{"class":6696},[1736,45562,6663],{"class":1912},[11,45564,45565],{},"Just by adding that line of code to your app and disabling the internet, either in dev tools or by turning off your internet connection, the message \"You are offline will appear\".",[11,45567,45568],{},"This won't work on page refresh and it only works when the page has loaded.",[11,45570,45571],{},"So what is it useful for? You could have a cool toast like message that pops out when the user loses internet and say something like \"ooopps looks like you just lost your internet connection\". This could very useful on payment pages or when filling out large forms for example. You still see the content of the page but are unaware that you have lost the connection.",[11,45573,45574,45575,45578],{},"Then you could have the ",[179,45576,45577],{},"$nuxt.isOnline"," to show a message that says \"yeah you are back online\".",[299,45580,45582],{"className":21974,"code":45581,"language":21976,"meta":307,"style":307},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cdiv v-if=\"$nuxt.isOnline\">Yeah you are online\u003C/div>\n    \u003Cnuxt />\n  \u003C/div>\n\u003C/template>\n",[179,45583,45584,45592,45600,45620,45628,45636],{"__ignoreMap":307},[1736,45585,45586,45588,45590],{"class":1738,"line":1739},[1736,45587,6657],{"class":1912},[1736,45589,21985],{"class":6696},[1736,45591,6663],{"class":1912},[1736,45593,45594,45596,45598],{"class":1738,"line":748},[1736,45595,7020],{"class":1912},[1736,45597,6697],{"class":6696},[1736,45599,6663],{"class":1912},[1736,45601,45602,45604,45606,45608,45610,45613,45616,45618],{"class":1738,"line":756},[1736,45603,6693],{"class":1912},[1736,45605,6697],{"class":6696},[1736,45607,39261],{"class":2674},[1736,45609,5062],{"class":1912},[1736,45611,45612],{"class":1935},"\"$nuxt.isOnline\"",[1736,45614,45615],{"class":1912},">Yeah you are online\u003C/",[1736,45617,6697],{"class":6696},[1736,45619,6663],{"class":1912},[1736,45621,45622,45624,45626],{"class":1738,"line":1755},[1736,45623,6693],{"class":1912},[1736,45625,5239],{"class":30490},[1736,45627,6739],{"class":1912},[1736,45629,45630,45632,45634],{"class":1738,"line":1761},[1736,45631,8096],{"class":1912},[1736,45633,6697],{"class":6696},[1736,45635,6663],{"class":1912},[1736,45637,45638,45640,45642],{"class":1738,"line":1767},[1736,45639,8105],{"class":1912},[1736,45641,21985],{"class":6696},[1736,45643,6663],{"class":1912},[11,45645,45646],{},"I am sure there are many other use cases for this and would love to hear if you have any suggestions or if anyone is actually using it and if so what for.",[11,45648,45649],{},"If you are not using it then go ahead and give it a try. In this example I have added it to the layout but you can add it to a page or even a component. Have fun.",[2011,45651,45652],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":45654},[],"2020-04-20","Did you know that `$nuxt.isOffline` can be used to show your users content based on if they are online or not.","photo-1588064718670-764b8515b86e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&",{},"/blog/nuxt-show-message-when-offline",{"title":45477,"description":45656},"blog/nuxt-show-message-when-offline",[5239],"DOhXE_D4bW25IsHK8_3YvMgaiptjBR86T5Mq8NnMqUQ",{"id":45665,"title":45666,"body":45667,"canonical":788,"date":21827,"description":45949,"extension":786,"featured":787,"image":39926,"meta":45950,"navigation":790,"ogimage":788,"path":45951,"provider":5235,"published":787,"seo":45952,"stem":45953,"tags":45954,"url":788,"__hash__":45955},"blog/blog/nuxt-using-apollo-to-fetch-data.md","Using Apollo to Fetch Data",{"type":8,"value":45668,"toc":45947},[45669,45778,45781,45837,45840,45887,45890,45944],[299,45670,45673],{"className":8734,"code":45671,"filename":45672,"language":8736,"meta":307,"style":307},"import { HttpLink } from 'apollo-link-http'\nimport { InMemoryCache } from 'apollo-cache-inmemory'\n\n// Replace this with your project's endpoint\nconst GRAPHCMS_API = 'https://debbie-codes.herokuapp.com/v1/graphql'\n\nexport default () => ({\n  link: new HttpLink({ uri: GRAPHCMS_API }),\n  cache: new InMemoryCache(),\n  defaultHttpLink: false\n})\n","apollo/client-configs.js",[179,45674,45675,45687,45699,45703,45708,45720,45724,45736,45755,45767,45774],{"__ignoreMap":307},[1736,45676,45677,45679,45682,45684],{"class":1738,"line":1739},[1736,45678,4996],{"class":4866},[1736,45680,45681],{"class":1912}," { HttpLink } ",[1736,45683,5002],{"class":4866},[1736,45685,45686],{"class":1935}," 'apollo-link-http'\n",[1736,45688,45689,45691,45694,45696],{"class":1738,"line":748},[1736,45690,4996],{"class":4866},[1736,45692,45693],{"class":1912}," { InMemoryCache } ",[1736,45695,5002],{"class":4866},[1736,45697,45698],{"class":1935}," 'apollo-cache-inmemory'\n",[1736,45700,45701],{"class":1738,"line":756},[1736,45702,1747],{"emptyLinePlaceholder":790},[1736,45704,45705],{"class":1738,"line":1755},[1736,45706,45707],{"class":6820},"// Replace this with your project's endpoint\n",[1736,45709,45710,45712,45715,45717],{"class":1738,"line":1761},[1736,45711,5029],{"class":4866},[1736,45713,45714],{"class":1918}," GRAPHCMS_API",[1736,45716,4911],{"class":4866},[1736,45718,45719],{"class":1935}," 'https://debbie-codes.herokuapp.com/v1/graphql'\n",[1736,45721,45722],{"class":1738,"line":1767},[1736,45723,1747],{"emptyLinePlaceholder":790},[1736,45725,45726,45728,45730,45732,45734],{"class":1738,"line":1772},[1736,45727,6632],{"class":4866},[1736,45729,30438],{"class":4866},[1736,45731,7010],{"class":1912},[1736,45733,7013],{"class":4866},[1736,45735,41782],{"class":1912},[1736,45737,45738,45741,45743,45746,45749,45752],{"class":1738,"line":1778},[1736,45739,45740],{"class":1912},"  link: ",[1736,45742,16628],{"class":4866},[1736,45744,45745],{"class":2674}," HttpLink",[1736,45747,45748],{"class":1912},"({ uri: ",[1736,45750,45751],{"class":1918},"GRAPHCMS_API",[1736,45753,45754],{"class":1912}," }),\n",[1736,45756,45757,45760,45762,45765],{"class":1738,"line":1784},[1736,45758,45759],{"class":1912},"  cache: ",[1736,45761,16628],{"class":4866},[1736,45763,45764],{"class":2674}," InMemoryCache",[1736,45766,34336],{"class":1912},[1736,45768,45769,45772],{"class":1738,"line":1790},[1736,45770,45771],{"class":1912},"  defaultHttpLink: ",[1736,45773,35177],{"class":1918},[1736,45775,45776],{"class":1738,"line":1796},[1736,45777,10582],{"class":1912},[11,45779,45780],{},"We need to use the graphql-tag to be able to add our query so first we import this import gql from 'graphql-tag' We can then make our query listing all the data that we want to receive and ordering by what we prefer. Here we just added an export const called workshop and make it equal to our gql tag which has a query called workshops and that queries the workshops table.",[299,45782,45784],{"className":8734,"code":45783,"filename":45672,"language":8736,"meta":307,"style":307},"export const workshops = gql`\n  query workshops {\n    workshops(order_by: { date: desc }) {\n      date\n      title\n      year\n    }\n  }\n`\n",[179,45785,45786,45801,45806,45810,45815,45820,45825,45829,45833],{"__ignoreMap":307},[1736,45787,45788,45790,45792,45795,45797,45799],{"class":1738,"line":1739},[1736,45789,6632],{"class":4866},[1736,45791,7002],{"class":4866},[1736,45793,45794],{"class":1918}," workshops",[1736,45796,4911],{"class":4866},[1736,45798,39747],{"class":2674},[1736,45800,39750],{"class":1935},[1736,45802,45803],{"class":1738,"line":748},[1736,45804,45805],{"class":1935},"  query workshops {\n",[1736,45807,45808],{"class":1738,"line":756},[1736,45809,39760],{"class":1935},[1736,45811,45812],{"class":1738,"line":1755},[1736,45813,45814],{"class":1935},"      date\n",[1736,45816,45817],{"class":1738,"line":1761},[1736,45818,45819],{"class":1935},"      title\n",[1736,45821,45822],{"class":1738,"line":1767},[1736,45823,45824],{"class":1935},"      year\n",[1736,45826,45827],{"class":1738,"line":1772},[1736,45828,9853],{"class":1935},[1736,45830,45831],{"class":1738,"line":1778},[1736,45832,1971],{"class":1935},[1736,45834,45835],{"class":1738,"line":1784},[1736,45836,39750],{"class":1935},[11,45838,45839],{},"Then we need to use apollo so that we can get our data to our template. Don't forget to first install '@nuxtjs/apollo', and then add to the modules of our next config.",[299,45841,45843],{"className":8734,"code":45842,"filename":45672,"language":8736,"meta":307,"style":307},"apollo: {\n    $loadingKey: 'loading',\n    workshops: {\n      query: workshops,\n    },\n  },\n",[179,45844,45845,45852,45864,45871,45879,45883],{"__ignoreMap":307},[1736,45846,45847,45850],{"class":1738,"line":1739},[1736,45848,45849],{"class":2674},"apollo",[1736,45851,1922],{"class":1912},[1736,45853,45854,45857,45859,45862],{"class":1738,"line":748},[1736,45855,45856],{"class":2674},"    $loadingKey",[1736,45858,3065],{"class":1912},[1736,45860,45861],{"class":1935},"'loading'",[1736,45863,1939],{"class":1912},[1736,45865,45866,45869],{"class":1738,"line":756},[1736,45867,45868],{"class":2674},"    workshops",[1736,45870,1922],{"class":1912},[1736,45872,45873,45876],{"class":1738,"line":1755},[1736,45874,45875],{"class":2674},"      query",[1736,45877,45878],{"class":1912},": workshops,\n",[1736,45880,45881],{"class":1738,"line":1761},[1736,45882,8553],{"class":1912},[1736,45884,45885],{"class":1738,"line":1767},[1736,45886,4929],{"class":1912},[11,45888,45889],{},"And now we can do a v-for over all our data and print the title for example",[299,45891,45893],{"className":8734,"code":45892,"filename":45672,"language":8736,"meta":307,"style":307},"\n\u003Cdiv v-for=\"(workshop, index) in workshops\" :key=\"index\" class=\"flex\">\n  \u003Cp>{{workshop.title}}\u003C/p>\n\u003C/div>\n",[179,45894,45895,45899,45923,45936],{"__ignoreMap":307},[1736,45896,45897],{"class":1738,"line":1739},[1736,45898,1747],{"emptyLinePlaceholder":790},[1736,45900,45901,45903,45905,45907,45909,45911,45914,45916,45918,45921],{"class":1738,"line":748},[1736,45902,6657],{"class":1912},[1736,45904,6697],{"class":6696},[1736,45906,39896],{"class":2674},[1736,45908,5062],{"class":4866},[1736,45910,39901],{"class":1935},[1736,45912,45913],{"class":30490}," :key=\"index\"",[1736,45915,36491],{"class":2674},[1736,45917,5062],{"class":4866},[1736,45919,45920],{"class":1935},"\"flex\"",[1736,45922,6663],{"class":1912},[1736,45924,45925,45927,45929,45932,45934],{"class":1738,"line":756},[1736,45926,7020],{"class":1912},[1736,45928,11],{"class":6696},[1736,45930,45931],{"class":1912},">{{workshop.title}}\u003C/",[1736,45933,11],{"class":6696},[1736,45935,6663],{"class":1912},[1736,45937,45938,45940,45942],{"class":1738,"line":1755},[1736,45939,8105],{"class":1912},[1736,45941,6697],{"class":6696},[1736,45943,6663],{"class":1912},[2011,45945,45946],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":45948},[],"Using Apollo to Fetch Data from a GraphQL API",{},"/blog/nuxt-using-apollo-to-fetch-data",{"title":45666,"description":45949},"blog/nuxt-using-apollo-to-fetch-data",[5239],"GiSOO8PU1qmAwBA4hk8jRO1XckiHjO7kK5XvNrLdxgU",{"id":45957,"title":45958,"body":45959,"canonical":788,"date":47190,"description":47191,"extension":786,"featured":787,"image":47192,"meta":47193,"navigation":790,"ogimage":788,"path":47195,"provider":3460,"published":790,"seo":47196,"stem":47197,"tags":47198,"url":788,"__hash__":47199},"blog/blog/pagination-in-nuxt-content.md","Pagination in Nuxt Content",{"type":8,"value":45960,"toc":47169},[45961,45964,45973,45977,45980,45984,45987,45994,46022,46026,46045,46059,46089,46096,46100,46107,46150,46154,46157,46212,46216,46219,46296,46300,46303,46397,46401,46404,46428,46432,46443,46473,46480,46731,46735,46744,46794,46798,46810,46838,46842,46860,46866,46873,46908,46918,46982,46986,46994,47052,47056,47072,47116,47118,47121,47124,47131,47133,47136,47166],[11,45962,45963],{},"Nuxt content is by far my favorite feature of Nuxt. It allows me to easily write my blog posts in Markdown format yet add components on to the page if and when I need to. And I love the live edit where I can simply click and edit the text directly in the browser and it saves the code for me. Mind blowing feature.",[11,45965,45966,45967,45972],{},"Writing my content in markdown makes it then easy for me to copy those posts and paste them to ",[15,45968,45971],{"href":45969,"rel":45970},"https://dev.to/debs_obrien",[19],"my dev.to"," account with a canonical link to my site. I don't need a content management tool to write my posts and am quite happy using Nuxt content to write my post, push it to GitHub and have Netlify build and publish a new version of my static site.",[23,45974,45976],{"id":45975},"why-pagination","Why Pagination",[11,45978,45979],{},"As I started to write more posts my blog page was getting very long and even though I had already split the posts into categories some of the categories were also getting quite long. This means the pages where handing a lot of content that needs to be scrolled and a lot of content that needs to be loaded on the page. Adding pagination would make for a better user experience but also better performance.",[23,45981,45983],{"id":45982},"querying-your-endpoint","Querying your Endpoint",[11,45985,45986],{},"The first thing I did was try to get pagination on the main blog page and then worry about getting it to work on the categories. Nuxt content will fetch my markdown files from a directory inside the content folder.",[11,45988,45989,45990,45993],{},"Remember you can test your queries locally by adding ",[179,45991,45992],{},"_content"," in your localhost URL and seeing the results of the data returned.",[299,45995,45997],{"className":2665,"code":45996,"language":2667,"meta":307,"style":307},"http://localhost:3000/_content/articles?only=title&limit=9&skip=9\n",[179,45998,45999],{"__ignoreMap":307},[1736,46000,46001,46004,46007,46010,46012,46014,46017,46019],{"class":1738,"line":1739},[1736,46002,46003],{"class":2674},"http://localhost:3000/_content/articles?only",[1736,46005,46006],{"class":1935},"=title",[1736,46008,46009],{"class":1912},"&limit",[1736,46011,5062],{"class":4866},[1736,46013,42138],{"class":1935},[1736,46015,46016],{"class":1912},"&skip",[1736,46018,5062],{"class":4866},[1736,46020,46021],{"class":1935},"9\n",[23,46023,46025],{"id":46024},"fetching-the-posts","Fetching the Posts",[11,46027,46028,46029,46031,46032,608,46034,46037,46038,46041,46042,46044],{},"To fetch the data we use ",[179,46030,39811],{}," passing in ",[179,46033,33316],{},[179,46035,46036],{},"params"," so we can access them from the Nuxt context. Then we add a const of ",[179,46039,46040],{},"pageNo"," which will get the number of the page from the params and we use ",[179,46043,12626],{}," to convert it to a number. Note: At the moment I am using Nuxt 2 until Nuxt 3 can support Nuxt content.",[11,46046,46047,46048,46051,46052,46054,46055,46058],{},"I want to get 9 Articles per page so we create a const called ",[179,46049,46050],{},"getArticles"," and then use the ",[179,46053,33316],{}," method passing in the folder to where my posts are stored. We then add a condition using ",[179,46056,46057],{},".where",". We want to make sure we only publish posts that do not have published set to false.",[299,46060,46062],{"className":8734,"code":46061,"language":8736,"meta":307,"style":307},"const getArticles = await $content('articles').fetch()\n",[179,46063,46064],{"__ignoreMap":307},[1736,46065,46066,46068,46071,46073,46075,46078,46080,46082,46084,46087],{"class":1738,"line":1739},[1736,46067,5029],{"class":4866},[1736,46069,46070],{"class":1918}," getArticles",[1736,46072,4911],{"class":4866},[1736,46074,18389],{"class":4866},[1736,46076,46077],{"class":2674}," $content",[1736,46079,7751],{"class":1912},[1736,46081,33361],{"class":1935},[1736,46083,911],{"class":1912},[1736,46085,46086],{"class":2674},"fetch",[1736,46088,22680],{"class":1912},[11,46090,46091,46092,46095],{},"Make sure you always add the ",[179,46093,46094],{},".fetch()"," at the end of your query. I have very often forgotten this and wondered why I wasn't getting any data back.",[138,46097,46099],{"id":46098},"fetch-only-posts-where-published-is-not-false","Fetch only posts where published is not false",[11,46101,46102,46103,46106],{},"In my posts I add ",[179,46104,46105],{},"published: false"," for those posts that are still a work in progress. That means I can still push them to GitHub but they won't get fetched by Nuxt content until I remove this from the yaml or set published to true. The reason I choose to use not equal to false instead of making it true was to basically not have to go back over all posts and add a condition to publish them.",[299,46108,46110],{"className":8734,"code":46109,"language":8736,"meta":307,"style":307},"const getArticles = await $content('articles')\n  .where({ published: { $ne: false } })\n  .fetch()\n",[179,46111,46112,46130,46142],{"__ignoreMap":307},[1736,46113,46114,46116,46118,46120,46122,46124,46126,46128],{"class":1738,"line":1739},[1736,46115,5029],{"class":4866},[1736,46117,46070],{"class":1918},[1736,46119,4911],{"class":4866},[1736,46121,18389],{"class":4866},[1736,46123,46077],{"class":2674},[1736,46125,7751],{"class":1912},[1736,46127,33361],{"class":1935},[1736,46129,7045],{"class":1912},[1736,46131,46132,46134,46136,46138,46140],{"class":1738,"line":748},[1736,46133,27458],{"class":1912},[1736,46135,33503],{"class":2674},[1736,46137,33506],{"class":1912},[1736,46139,33509],{"class":1918},[1736,46141,33512],{"class":1912},[1736,46143,46144,46146,46148],{"class":1738,"line":756},[1736,46145,27458],{"class":1912},[1736,46147,46086],{"class":2674},[1736,46149,22680],{"class":1912},[138,46151,46153],{"id":46152},"limit-the-amount-of-posts-returned","Limit the amount of posts returned",[11,46155,46156],{},"Next we want to limit the amount of posts that come back so that we only have 9 posts per page.",[299,46158,46160],{"className":8734,"code":46159,"language":8736,"meta":307,"style":307},"const getArticles = await $content('articles')\n  .where({ published: { $ne: false } })\n  .limit(9)\n  .fetch()\n",[179,46161,46162,46180,46192,46204],{"__ignoreMap":307},[1736,46163,46164,46166,46168,46170,46172,46174,46176,46178],{"class":1738,"line":1739},[1736,46165,5029],{"class":4866},[1736,46167,46070],{"class":1918},[1736,46169,4911],{"class":4866},[1736,46171,18389],{"class":4866},[1736,46173,46077],{"class":2674},[1736,46175,7751],{"class":1912},[1736,46177,33361],{"class":1935},[1736,46179,7045],{"class":1912},[1736,46181,46182,46184,46186,46188,46190],{"class":1738,"line":748},[1736,46183,27458],{"class":1912},[1736,46185,33503],{"class":2674},[1736,46187,33506],{"class":1912},[1736,46189,33509],{"class":1918},[1736,46191,33512],{"class":1912},[1736,46193,46194,46196,46198,46200,46202],{"class":1738,"line":756},[1736,46195,27458],{"class":1912},[1736,46197,33562],{"class":2674},[1736,46199,7751],{"class":1912},[1736,46201,42138],{"class":1918},[1736,46203,7045],{"class":1912},[1736,46205,46206,46208,46210],{"class":1738,"line":1755},[1736,46207,27458],{"class":1912},[1736,46209,46086],{"class":2674},[1736,46211,22680],{"class":1912},[138,46213,46215],{"id":46214},"skip-the-posts-based-on-page-number","Skip the posts based on page number",[11,46217,46218],{},"We then add a condition to skip the first 9 posts times the page number -1 so if on page 1, don't skip any. If on page 2 skip 9 etc. This is because we want to show the first page of posts and then the second page of posts and so on.",[299,46220,46222],{"className":8734,"code":46221,"language":8736,"meta":307,"style":307},"const getArticles = await $content('articles')\n  .where({ published: { $ne: false } })\n  .limit(9)\n  .skip(9 * (pageNo - 1))\n  .fetch()\n",[179,46223,46224,46242,46254,46266,46288],{"__ignoreMap":307},[1736,46225,46226,46228,46230,46232,46234,46236,46238,46240],{"class":1738,"line":1739},[1736,46227,5029],{"class":4866},[1736,46229,46070],{"class":1918},[1736,46231,4911],{"class":4866},[1736,46233,18389],{"class":4866},[1736,46235,46077],{"class":2674},[1736,46237,7751],{"class":1912},[1736,46239,33361],{"class":1935},[1736,46241,7045],{"class":1912},[1736,46243,46244,46246,46248,46250,46252],{"class":1738,"line":748},[1736,46245,27458],{"class":1912},[1736,46247,33503],{"class":2674},[1736,46249,33506],{"class":1912},[1736,46251,33509],{"class":1918},[1736,46253,33512],{"class":1912},[1736,46255,46256,46258,46260,46262,46264],{"class":1738,"line":756},[1736,46257,27458],{"class":1912},[1736,46259,33562],{"class":2674},[1736,46261,7751],{"class":1912},[1736,46263,42138],{"class":1918},[1736,46265,7045],{"class":1912},[1736,46267,46268,46270,46272,46274,46276,46279,46282,46284,46286],{"class":1738,"line":1755},[1736,46269,27458],{"class":1912},[1736,46271,33533],{"class":2674},[1736,46273,7751],{"class":1912},[1736,46275,42138],{"class":1918},[1736,46277,46278],{"class":4866}," *",[1736,46280,46281],{"class":1912}," (pageNo ",[1736,46283,9419],{"class":4866},[1736,46285,9722],{"class":1918},[1736,46287,39638],{"class":1912},[1736,46289,46290,46292,46294],{"class":1738,"line":1761},[1736,46291,27458],{"class":1912},[1736,46293,46086],{"class":2674},[1736,46295,22680],{"class":1912},[138,46297,46299],{"id":46298},"sort-the-posts-by-date","Sort the posts by date",[11,46301,46302],{},"Next we sort the posts by date in descending order so that the newest posts are on top.",[299,46304,46306],{"className":8734,"code":46305,"language":8736,"meta":307,"style":307},"const getArticles = await $content('articles')\n  .where({ published: { $ne: false } })\n  .limit(9)\n  .skip(9 * (pageNo - 1))\n  .sortBy('date', 'desc')\n  .fetch()\n",[179,46307,46308,46326,46338,46350,46370,46389],{"__ignoreMap":307},[1736,46309,46310,46312,46314,46316,46318,46320,46322,46324],{"class":1738,"line":1739},[1736,46311,5029],{"class":4866},[1736,46313,46070],{"class":1918},[1736,46315,4911],{"class":4866},[1736,46317,18389],{"class":4866},[1736,46319,46077],{"class":2674},[1736,46321,7751],{"class":1912},[1736,46323,33361],{"class":1935},[1736,46325,7045],{"class":1912},[1736,46327,46328,46330,46332,46334,46336],{"class":1738,"line":748},[1736,46329,27458],{"class":1912},[1736,46331,33503],{"class":2674},[1736,46333,33506],{"class":1912},[1736,46335,33509],{"class":1918},[1736,46337,33512],{"class":1912},[1736,46339,46340,46342,46344,46346,46348],{"class":1738,"line":756},[1736,46341,27458],{"class":1912},[1736,46343,33562],{"class":2674},[1736,46345,7751],{"class":1912},[1736,46347,42138],{"class":1918},[1736,46349,7045],{"class":1912},[1736,46351,46352,46354,46356,46358,46360,46362,46364,46366,46368],{"class":1738,"line":1755},[1736,46353,27458],{"class":1912},[1736,46355,33533],{"class":2674},[1736,46357,7751],{"class":1912},[1736,46359,42138],{"class":1918},[1736,46361,46278],{"class":4866},[1736,46363,46281],{"class":1912},[1736,46365,9419],{"class":4866},[1736,46367,9722],{"class":1918},[1736,46369,39638],{"class":1912},[1736,46371,46372,46374,46377,46379,46382,46384,46387],{"class":1738,"line":1761},[1736,46373,27458],{"class":1912},[1736,46375,46376],{"class":2674},"sortBy",[1736,46378,7751],{"class":1912},[1736,46380,46381],{"class":1935},"'date'",[1736,46383,829],{"class":1912},[1736,46385,46386],{"class":1935},"'desc'",[1736,46388,7045],{"class":1912},[1736,46390,46391,46393,46395],{"class":1738,"line":1767},[1736,46392,27458],{"class":1912},[1736,46394,46086],{"class":2674},[1736,46396,22680],{"class":1912},[138,46398,46400],{"id":46399},"set-the-next-page","Set the next page",[11,46402,46403],{},"Next page is set to true if the amount of articles received is equal to 9. This means we can then render our next page button if the condition is true.",[299,46405,46407],{"className":8734,"code":46406,"language":8736,"meta":307,"style":307},"const nextPage = getArticles.length === 9\n",[179,46408,46409],{"__ignoreMap":307},[1736,46410,46411,46413,46416,46418,46421,46423,46425],{"class":1738,"line":1739},[1736,46412,5029],{"class":4866},[1736,46414,46415],{"class":1918}," nextPage",[1736,46417,4911],{"class":4866},[1736,46419,46420],{"class":1912}," getArticles.",[1736,46422,18450],{"class":1918},[1736,46424,18453],{"class":4866},[1736,46426,46427],{"class":1918}," 9\n",[138,46429,46431],{"id":46430},"return-what-we-need","Return what we need",[11,46433,46434,46435,46437,46438,10351,46441,891],{},"Our final step and one of the most important is to return our data which is our const of ",[179,46436,46050],{}," as well as return our ",[179,46439,46440],{},"nextPage",[179,46442,46040],{},[299,46444,46446],{"className":8734,"code":46445,"language":8736,"meta":307,"style":307},"return {\n  nextPage,\n  getArticles,\n  pageNo\n}\n",[179,46447,46448,46454,46459,46464,46469],{"__ignoreMap":307},[1736,46449,46450,46452],{"class":1738,"line":1739},[1736,46451,11860],{"class":4866},[1736,46453,4914],{"class":1912},[1736,46455,46456],{"class":1738,"line":748},[1736,46457,46458],{"class":1912},"  nextPage,\n",[1736,46460,46461],{"class":1738,"line":756},[1736,46462,46463],{"class":1912},"  getArticles,\n",[1736,46465,46466],{"class":1738,"line":1755},[1736,46467,46468],{"class":1912},"  pageNo\n",[1736,46470,46471],{"class":1738,"line":1761},[1736,46472,1976],{"class":1912},[11,46474,46475,46476,46479],{},"The final code looks something like this. Note I have the layout properties in here so that all my blog pages use the same layout which I named blog. I also added a const called ",[179,46477,46478],{},"numArticles"," making it equal to 9 just to keep things dry and finally I added an if statement to deal with errors incase there are no articles returned. This will render my error page with the message of 'no articles found'",[299,46481,46483],{"className":4894,"code":46482,"language":4896,"meta":307,"style":307},"export default {\n  layout: 'blog',\n  async asyncData({ $content, params }) {\n    const pageNo = parseInt(params.number)\n    const numArticles = 9\n\n    const getArticles = await $content('articles')\n      .where({ published: { $ne: false } })\n      .limit(numArticles)\n      .skip(numArticles * (pageNo - 1))\n      .sortBy('date', 'desc')\n      .fetch()\n\n    if (!getArticles.length)\n      return error({ statusCode: 404, message: 'No articles found!' })\n\n    const nextPage = getArticles.length === numArticles\n    getArticles\n    return {\n      nextPage,\n      getArticles,\n      pageNo\n    }\n  }\n}\n",[179,46484,46485,46493,46502,46519,46534,46545,46549,46567,46580,46589,46608,46624,46632,46636,46651,46672,46676,46693,46698,46704,46709,46714,46719,46723,46727],{"__ignoreMap":307},[1736,46486,46487,46489,46491],{"class":1738,"line":1739},[1736,46488,6632],{"class":4866},[1736,46490,30438],{"class":4866},[1736,46492,4914],{"class":1912},[1736,46494,46495,46498,46500],{"class":1738,"line":748},[1736,46496,46497],{"class":1912},"  layout: ",[1736,46499,33378],{"class":1935},[1736,46501,1939],{"class":1912},[1736,46503,46504,46506,46509,46511,46513,46515,46517],{"class":1738,"line":756},[1736,46505,16127],{"class":4866},[1736,46507,46508],{"class":2674}," asyncData",[1736,46510,7233],{"class":1912},[1736,46512,33316],{"class":5036},[1736,46514,829],{"class":1912},[1736,46516,46036],{"class":5036},[1736,46518,31433],{"class":1912},[1736,46520,46521,46523,46526,46528,46531],{"class":1738,"line":1755},[1736,46522,14497],{"class":4866},[1736,46524,46525],{"class":1918}," pageNo",[1736,46527,4911],{"class":4866},[1736,46529,46530],{"class":2674}," parseInt",[1736,46532,46533],{"class":1912},"(params.number)\n",[1736,46535,46536,46538,46541,46543],{"class":1738,"line":1761},[1736,46537,14497],{"class":4866},[1736,46539,46540],{"class":1918}," numArticles",[1736,46542,4911],{"class":4866},[1736,46544,46427],{"class":1918},[1736,46546,46547],{"class":1738,"line":1767},[1736,46548,1747],{"emptyLinePlaceholder":790},[1736,46550,46551,46553,46555,46557,46559,46561,46563,46565],{"class":1738,"line":1772},[1736,46552,14497],{"class":4866},[1736,46554,46070],{"class":1918},[1736,46556,4911],{"class":4866},[1736,46558,18389],{"class":4866},[1736,46560,46077],{"class":2674},[1736,46562,7751],{"class":1912},[1736,46564,33361],{"class":1935},[1736,46566,7045],{"class":1912},[1736,46568,46569,46572,46574,46576,46578],{"class":1738,"line":1778},[1736,46570,46571],{"class":1912},"      .",[1736,46573,33503],{"class":2674},[1736,46575,33506],{"class":1912},[1736,46577,33509],{"class":1918},[1736,46579,33512],{"class":1912},[1736,46581,46582,46584,46586],{"class":1738,"line":1784},[1736,46583,46571],{"class":1912},[1736,46585,33562],{"class":2674},[1736,46587,46588],{"class":1912},"(numArticles)\n",[1736,46590,46591,46593,46595,46598,46600,46602,46604,46606],{"class":1738,"line":1790},[1736,46592,46571],{"class":1912},[1736,46594,33533],{"class":2674},[1736,46596,46597],{"class":1912},"(numArticles ",[1736,46599,14968],{"class":4866},[1736,46601,46281],{"class":1912},[1736,46603,9419],{"class":4866},[1736,46605,9722],{"class":1918},[1736,46607,39638],{"class":1912},[1736,46609,46610,46612,46614,46616,46618,46620,46622],{"class":1738,"line":1796},[1736,46611,46571],{"class":1912},[1736,46613,46376],{"class":2674},[1736,46615,7751],{"class":1912},[1736,46617,46381],{"class":1935},[1736,46619,829],{"class":1912},[1736,46621,46386],{"class":1935},[1736,46623,7045],{"class":1912},[1736,46625,46626,46628,46630],{"class":1738,"line":2353},[1736,46627,46571],{"class":1912},[1736,46629,46086],{"class":2674},[1736,46631,22680],{"class":1912},[1736,46633,46634],{"class":1738,"line":2358},[1736,46635,1747],{"emptyLinePlaceholder":790},[1736,46637,46638,46640,46642,46644,46647,46649],{"class":1738,"line":2364},[1736,46639,9817],{"class":4866},[1736,46641,1095],{"class":1912},[1736,46643,1690],{"class":4866},[1736,46645,46646],{"class":1912},"getArticles.",[1736,46648,18450],{"class":1918},[1736,46650,7045],{"class":1912},[1736,46652,46653,46655,46658,46661,46664,46667,46670],{"class":1738,"line":2370},[1736,46654,15801],{"class":4866},[1736,46656,46657],{"class":2674}," error",[1736,46659,46660],{"class":1912},"({ statusCode: ",[1736,46662,46663],{"class":1918},"404",[1736,46665,46666],{"class":1912},", message: ",[1736,46668,46669],{"class":1935},"'No articles found!'",[1736,46671,10691],{"class":1912},[1736,46673,46674],{"class":1738,"line":2376},[1736,46675,1747],{"emptyLinePlaceholder":790},[1736,46677,46678,46680,46682,46684,46686,46688,46690],{"class":1738,"line":2381},[1736,46679,14497],{"class":4866},[1736,46681,46415],{"class":1918},[1736,46683,4911],{"class":4866},[1736,46685,46420],{"class":1912},[1736,46687,18450],{"class":1918},[1736,46689,18453],{"class":4866},[1736,46691,46692],{"class":1912}," numArticles\n",[1736,46694,46695],{"class":1738,"line":2387},[1736,46696,46697],{"class":1912},"    getArticles\n",[1736,46699,46700,46702],{"class":1738,"line":2393},[1736,46701,14242],{"class":4866},[1736,46703,4914],{"class":1912},[1736,46705,46706],{"class":1738,"line":2398},[1736,46707,46708],{"class":1912},"      nextPage,\n",[1736,46710,46711],{"class":1738,"line":2404},[1736,46712,46713],{"class":1912},"      getArticles,\n",[1736,46715,46716],{"class":1738,"line":6959},[1736,46717,46718],{"class":1912},"      pageNo\n",[1736,46720,46721],{"class":1738,"line":7296},[1736,46722,9853],{"class":1912},[1736,46724,46725],{"class":1738,"line":7305},[1736,46726,1971],{"class":1912},[1736,46728,46729],{"class":1738,"line":7310},[1736,46730,1976],{"class":1912},[23,46732,46734],{"id":46733},"rendering-the-posts","Rendering the posts",[11,46736,46737,46738,46740,46741,46743],{},"The next step is to render the posts. We do this by using ",[179,46739,30901],{}," and looping over the ",[179,46742,46050],{}," and rendering each article using the 'PostsCard' component.",[299,46745,46747],{"className":4894,"code":46746,"language":4896,"meta":307,"style":307},"\u003Cdiv v-for=\"article of getArticles\" :key=\"article.slug\" class=\"flex flex-col\">\n  \u003CBlogPostCard :item=\"article\" />\n\u003C/div>\n",[179,46748,46749,46774,46786],{"__ignoreMap":307},[1736,46750,46751,46753,46755,46757,46759,46762,46765,46767,46769,46772],{"class":1738,"line":1739},[1736,46752,6657],{"class":1912},[1736,46754,6697],{"class":6696},[1736,46756,39896],{"class":2674},[1736,46758,5062],{"class":4866},[1736,46760,46761],{"class":1935},"\"article of getArticles\"",[1736,46763,46764],{"class":30490}," :key=\"article.slug\"",[1736,46766,36491],{"class":2674},[1736,46768,5062],{"class":4866},[1736,46770,46771],{"class":1935},"\"flex flex-col\"",[1736,46773,6663],{"class":1912},[1736,46775,46776,46778,46781,46784],{"class":1738,"line":748},[1736,46777,7020],{"class":1912},[1736,46779,46780],{"class":1918},"BlogPostCard",[1736,46782,46783],{"class":30490}," :item=\"article\"",[1736,46785,6739],{"class":1912},[1736,46787,46788,46790,46792],{"class":1738,"line":756},[1736,46789,8105],{"class":1912},[1736,46791,6697],{"class":6696},[1736,46793,6663],{"class":1912},[138,46795,46797],{"id":46796},"rendering-the-pagination-component","Rendering the Pagination Component",[11,46799,46800,46801,46803,46804,46806,46807,46809],{},"We then render the pagination component which has a prop of ",[179,46802,46440],{}," and a prop of ",[179,46805,46040],{},". We want the first page to be 1 and the ",[179,46808,46440],{}," will be either true or false depending on if the length of our articles is equal to 9.",[299,46811,46813],{"className":4894,"code":46812,"language":4896,"meta":307,"style":307},"\u003CPagination :nextPage=\"nextPage\" :pageNo=\"1\" urlPrefix=\"/blog/all\" />\n",[179,46814,46815],{"__ignoreMap":307},[1736,46816,46817,46819,46822,46825,46828,46831,46833,46836],{"class":1738,"line":1739},[1736,46818,6657],{"class":1912},[1736,46820,46821],{"class":1918},"Pagination",[1736,46823,46824],{"class":30490}," :nextPage=\"nextPage\"",[1736,46826,46827],{"class":30490}," :pageNo=\"1\"",[1736,46829,46830],{"class":2674}," urlPrefix",[1736,46832,5062],{"class":4866},[1736,46834,46835],{"class":1935},"\"/blog/all\"",[1736,46837,6739],{"class":1912},[23,46839,46841],{"id":46840},"creating-dynamic-category-pages","Creating Dynamic Category Pages",[11,46843,46844,46845,46848,46849,46852,46853,46856,46857,891],{},"We have pagination on the main blog page but now we need to create pages for each category so we can have pagination for the Nuxt category, React category, Testing category etc. In Nuxt we can create dynamic pages by creating a folder with ",[179,46846,46847],{},"_category"," and inside it a folder with ",[179,46850,46851],{},"_number",". This will give you a url of ",[179,46854,46855],{},"/blog/category/number"," but as it is dynamic it will render something like this ",[179,46858,46859],{},"/blog/nuxt/1",[11,46861,46862,46863,46865],{},"We then create an index file inside the ",[179,46864,46851],{}," folder. This will be the page that gets rendered containing the blog posts for that category.",[11,46867,46868,46869,46872],{},"The main difference between this and the main blog page is adding the ",[179,46870,46871],{},"selectedTag"," to our data with the value of the category we get back from our route params.",[299,46874,46876],{"className":4894,"code":46875,"language":4896,"meta":307,"style":307},"data() {\n      return {\n        selectedTag: this.$route.params.category\n      }\n    },\n\n",[179,46877,46878,46884,46890,46900,46904],{"__ignoreMap":307},[1736,46879,46880,46882],{"class":1738,"line":1739},[1736,46881,33342],{"class":2674},[1736,46883,6680],{"class":1912},[1736,46885,46886,46888],{"class":1738,"line":748},[1736,46887,15801],{"class":4866},[1736,46889,4914],{"class":1912},[1736,46891,46892,46895,46897],{"class":1738,"line":756},[1736,46893,46894],{"class":1912},"        selectedTag: ",[1736,46896,35213],{"class":1918},[1736,46898,46899],{"class":1912},".$route.params.category\n",[1736,46901,46902],{"class":1738,"line":1755},[1736,46903,14448],{"class":1912},[1736,46905,46906],{"class":1738,"line":1761},[1736,46907,8553],{"class":1912},[11,46909,46910,46911,46914,46915,891],{},"We also need to add a computed property to filter the articles by the selected Tag. Using the ",[179,46912,46913],{},".filter()"," method it will go through each article to see if the selected Tag, which we get from our route params, is found inside the tags array that is added to the yaml of each article. The tags array looks something like this ",[179,46916,46917],{},"tags: [nuxt]",[299,46919,46921],{"className":4894,"code":46920,"language":4896,"meta":307,"style":307},"    computed: {\n      filteredArticles() {\n        return this.getArticles.filter(article =>\n          article.tags.includes(this.selectedTag)\n        )\n      }\n    }\n",[179,46922,46923,46930,46937,46956,46970,46974,46978],{"__ignoreMap":307},[1736,46924,46925,46928],{"class":1738,"line":1739},[1736,46926,46927],{"class":2674},"    computed",[1736,46929,1922],{"class":1912},[1736,46931,46932,46935],{"class":1738,"line":748},[1736,46933,46934],{"class":2674},"      filteredArticles",[1736,46936,6680],{"class":1912},[1736,46938,46939,46941,46943,46946,46948,46950,46953],{"class":1738,"line":756},[1736,46940,14749],{"class":4866},[1736,46942,16156],{"class":1918},[1736,46944,46945],{"class":1912},".getArticles.",[1736,46947,14507],{"class":2674},[1736,46949,7751],{"class":1912},[1736,46951,46952],{"class":5036},"article",[1736,46954,46955],{"class":4866}," =>\n",[1736,46957,46958,46961,46963,46965,46967],{"class":1738,"line":1755},[1736,46959,46960],{"class":1912},"          article.tags.",[1736,46962,34757],{"class":2674},[1736,46964,7751],{"class":1912},[1736,46966,35213],{"class":1918},[1736,46968,46969],{"class":1912},".selectedTag)\n",[1736,46971,46972],{"class":1738,"line":1761},[1736,46973,14865],{"class":1912},[1736,46975,46976],{"class":1738,"line":1767},[1736,46977,14448],{"class":1912},[1736,46979,46980],{"class":1738,"line":1772},[1736,46981,9853],{"class":1912},[23,46983,46985],{"id":46984},"rendering-our-filtered-posts","Rendering our filtered posts",[11,46987,46988,46989,34192,46992,891],{},"Now when rendering our posts we need to use the ",[179,46990,46991],{},"filteredArticles",[179,46993,46050],{},[299,46995,46997],{"className":4894,"code":46996,"language":4896,"meta":307,"style":307},"\u003Cdiv\n  v-for=\"article of filteredArticles\"\n  :key=\"article.slug\"\n  class=\"flex flex-col\"\n>\n  \u003CBlogPostCard :item=\"article\" />\n\u003C/div>\n",[179,46998,46999,47006,47016,47021,47030,47034,47044],{"__ignoreMap":307},[1736,47000,47001,47003],{"class":1738,"line":1739},[1736,47002,6657],{"class":1912},[1736,47004,47005],{"class":6696},"div\n",[1736,47007,47008,47011,47013],{"class":1738,"line":748},[1736,47009,47010],{"class":2674},"  v-for",[1736,47012,5062],{"class":4866},[1736,47014,47015],{"class":1935},"\"article of filteredArticles\"\n",[1736,47017,47018],{"class":1738,"line":756},[1736,47019,47020],{"class":30490},"  :key=\"article.slug\"\n",[1736,47022,47023,47025,47027],{"class":1738,"line":1755},[1736,47024,40430],{"class":2674},[1736,47026,5062],{"class":4866},[1736,47028,47029],{"class":1935},"\"flex flex-col\"\n",[1736,47031,47032],{"class":1738,"line":1761},[1736,47033,6663],{"class":1912},[1736,47035,47036,47038,47040,47042],{"class":1738,"line":1767},[1736,47037,7020],{"class":1912},[1736,47039,46780],{"class":1918},[1736,47041,46783],{"class":30490},[1736,47043,6739],{"class":1912},[1736,47045,47046,47048,47050],{"class":1738,"line":1772},[1736,47047,8105],{"class":1912},[1736,47049,6697],{"class":6696},[1736,47051,6663],{"class":1912},[138,47053,47055],{"id":47054},"rendering-the-pagination","Rendering the pagination",[11,47057,47058,47059,47062,47063,608,47065,47067,47068,47071],{},"For our pagination component we need to pass in the prop of ",[179,47060,47061],{},"prevPage"," and set it to true of false if the page number is greater than 1. We also pass in our ",[179,47064,46440],{},[179,47066,46040],{}," props and finally our ",[179,47069,47070],{},"urlPrefix"," which gets our category from the route params.",[299,47073,47075],{"className":4894,"code":47074,"language":4896,"meta":307,"style":307},"\u003CPagination\n  :prevPage=\"pageNo > 1\"\n  :nextPage=\"nextPage\"\n  :pageNo=\"pageNo\"\n  :urlPrefix=\"`/blog/${this.$route.params.category}`\"\n/>\n",[179,47076,47077,47084,47092,47097,47102,47112],{"__ignoreMap":307},[1736,47078,47079,47081],{"class":1738,"line":1739},[1736,47080,6657],{"class":1912},[1736,47082,47083],{"class":1918},"Pagination\n",[1736,47085,47086,47089],{"class":1738,"line":748},[1736,47087,47088],{"class":30490},"  :prevPage=\"pageNo",[1736,47090,47091],{"class":1912}," > 1\"\n",[1736,47093,47094],{"class":1738,"line":756},[1736,47095,47096],{"class":1912},"  :nextPage=\"nextPage\"\n",[1736,47098,47099],{"class":1738,"line":1755},[1736,47100,47101],{"class":1912},"  :pageNo=\"pageNo\"\n",[1736,47103,47104,47107,47109],{"class":1738,"line":1761},[1736,47105,47106],{"class":1912},"  :urlPrefix=\"`/blog/${",[1736,47108,35213],{"class":1918},[1736,47110,47111],{"class":1912},".$route.params.category}`\"\n",[1736,47113,47114],{"class":1738,"line":1767},[1736,47115,23222],{"class":1912},[23,47117,3294],{"id":3293},[11,47119,47120],{},"I now have pagination running on my blog and a page for each category and number. These pages are dynamic and upon building my static site, Nuxt will pre-render a page for each of these dynamic pages. This will enhance performance and give users a much better experience.",[11,47122,47123],{},"There is one thing I am not too happy with. My main blog page is practically a copy of the dynamic index page under the category/number folder. This means I have to maintain this code twice and that is never good. There are a few ways around this.",[11,47125,47126,47127,47130],{},"I could create a middleware that intercepts this route and brings me to the all category page 1 route. I could also create a Netlify redirect that will do the same thing. However I do like having the blog page route of just ",[179,47128,47129],{},"/blog"," so I am undecided on what the best solution here is. If you have any suggestions let me know.",[23,47132,5706],{"id":5705},[11,47134,47135],{},"My code is fully open source so clone, copy, or use whatever you like. Have fun.",[70,47137,47138,47145,47152,47159],{},[73,47139,47140],{},[15,47141,47144],{"href":47142,"rel":47143},"https://github.com/debs-obrien/debbie.codes/tree/master/pages/blog",[19],"Code on GitHub",[73,47146,47147],{},[15,47148,47151],{"href":47149,"rel":47150},"https://debbie.codes/blog",[19],"Blog Page",[73,47153,47154],{},[15,47155,47158],{"href":47156,"rel":47157},"https://content.nuxtjs.org/advanced#api-endpoint",[19],"Nuxt Content API Endpoint",[73,47160,47161],{},[15,47162,47165],{"href":47163,"rel":47164},"https://nuxtjs.org/tutorials/creating-blog-with-nuxt-content",[19],"Crate a Blog with Nuxt Content",[2011,47167,47168],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":307,"searchDepth":748,"depth":748,"links":47170},[47171,47172,47173,47181,47184,47185,47188,47189],{"id":45975,"depth":748,"text":45976},{"id":45982,"depth":748,"text":45983},{"id":46024,"depth":748,"text":46025,"children":47174},[47175,47176,47177,47178,47179,47180],{"id":46098,"depth":756,"text":46099},{"id":46152,"depth":756,"text":46153},{"id":46214,"depth":756,"text":46215},{"id":46298,"depth":756,"text":46299},{"id":46399,"depth":756,"text":46400},{"id":46430,"depth":756,"text":46431},{"id":46733,"depth":748,"text":46734,"children":47182},[47183],{"id":46796,"depth":756,"text":46797},{"id":46840,"depth":748,"text":46841},{"id":46984,"depth":748,"text":46985,"children":47186},[47187],{"id":47054,"depth":756,"text":47055},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2022-03-26","My blog was getting bigger and bigger and so time to add pagination. I am using Nuxt Content to manage my blog posts. So how do I add pagination to my blog? Let me show you how I did it.","v1648296331/debbie.codes/blog/2022/pagination_2x_ppog9g.png",{"ogImage":47194,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648296331/debbie.codes/blog/2022/pagination_2x_ppog9g.png","/blog/pagination-in-nuxt-content",{"title":45958,"description":47191},"blog/pagination-in-nuxt-content",[5239],"05Lc1FmIfrEImgFp_q2goKdLnnyAu_WpVbH0g4Vd0wg",{"id":47201,"title":47202,"body":47203,"canonical":47509,"date":47510,"description":47511,"extension":786,"featured":787,"image":788,"meta":47512,"navigation":790,"ogimage":788,"path":47513,"provider":788,"published":790,"seo":47514,"stem":47515,"tags":47516,"url":788,"__hash__":47517},"blog/blog/playwright-agents-planner-generator-and-healer-in-action.md","Playwright Agents Planner, Generator, and Healer in Action",{"type":8,"value":47204,"toc":47495},[47205,47208,47211,47215,47222,47239,47242,47246,47249,47252,47361,47368,47372,47379,47382,47386,47392,47395,47399,47402,47405,47408,47414,47417,47420,47423,47427,47430,47433,47439,47442,47444,47447,47450,47466,47469,47486,47489,47492],[11,47206,47207],{},"Playwright has introduced a powerful new feature: Playwright Agents, new in Version 1.56. These agents can generate test plans, create tests based on those plans, and even debug and fix failing tests automatically. This post demonstrates how Planner, Generator, and Healer agents work together using a movies application as an example.",[11,47209,47210],{},"• 🎭 Planner generates comprehensive test plans.\n• 🎭 Generator produces runnable test files from the plans.\n• 🎭 Healer debugs and fixes failing tests automatically.",[23,47212,47214],{"id":47213},"getting-started-with-playwright-agents","Getting Started with Playwright Agents",[11,47216,47217,47218,47221],{},"To begin make sure you have the latest version of Playwright installed. Then, run the ",[179,47219,47220],{},"init-agents"," command with your preferred loop, vscode, claude, or opencode:",[299,47223,47225],{"className":2665,"code":47224,"language":2667,"meta":307,"style":307},"npx playwright init-agents --loop=vscode\n",[179,47226,47227],{"__ignoreMap":307},[1736,47228,47229,47231,47233,47236],{"class":1738,"line":1739},[1736,47230,2675],{"class":2674},[1736,47232,22593],{"class":1935},[1736,47234,47235],{"class":1935}," init-agents",[1736,47237,47238],{"class":1918}," --loop=vscode\n",[11,47240,47241],{},"This command generates the agents along with a seed file.",[23,47243,47245],{"id":47244},"seeding-your-tests","Seeding your tests",[11,47247,47248],{},"The seed file is used to seed tests and will be copied into generated tests by the agent. It can be left blank, but it is typically used to include fixtures or setup logic that must run before tests.",[11,47250,47251],{},"For example, in a movies application, a fixture can be defined to create a page containing a list of movies. This page becomes the starting point for the tests and is copied into each generated test file.",[299,47253,47255],{"className":5635,"code":47254,"language":5637,"meta":307,"style":307},"/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { listTest as test } from '../helpers/list-test';\nimport { expect } from '@playwright/test';\n\ntest.describe('seed for logged in user', () => {\n  test('seed using listPage fixture', async ({ listPage }) => {\n    const page = listPage; // set the page to the listPage fixture\n  });\n});\n",[179,47256,47257,47262,47281,47294,47298,47315,47339,47353,47357],{"__ignoreMap":307},[1736,47258,47259],{"class":1738,"line":1739},[1736,47260,47261],{"class":6820},"/* eslint-disable @typescript-eslint/no-unused-vars */\n",[1736,47263,47264,47266,47269,47271,47274,47276,47279],{"class":1738,"line":748},[1736,47265,4996],{"class":4866},[1736,47267,47268],{"class":1912}," { listTest ",[1736,47270,7843],{"class":4866},[1736,47272,47273],{"class":1912}," test } ",[1736,47275,5002],{"class":4866},[1736,47277,47278],{"class":1935}," '../helpers/list-test'",[1736,47280,7682],{"class":1912},[1736,47282,47283,47285,47288,47290,47292],{"class":1738,"line":756},[1736,47284,4996],{"class":4866},[1736,47286,47287],{"class":1912}," { expect } ",[1736,47289,5002],{"class":4866},[1736,47291,25282],{"class":1935},[1736,47293,7682],{"class":1912},[1736,47295,47296],{"class":1738,"line":1755},[1736,47297,1747],{"emptyLinePlaceholder":790},[1736,47299,47300,47302,47304,47306,47309,47311,47313],{"class":1738,"line":1761},[1736,47301,32280],{"class":1912},[1736,47303,17717],{"class":2674},[1736,47305,7751],{"class":1912},[1736,47307,47308],{"class":1935},"'seed for logged in user'",[1736,47310,10524],{"class":1912},[1736,47312,7013],{"class":4866},[1736,47314,4914],{"class":1912},[1736,47316,47317,47319,47321,47324,47326,47328,47330,47333,47335,47337],{"class":1738,"line":1767},[1736,47318,32298],{"class":2674},[1736,47320,7751],{"class":1912},[1736,47322,47323],{"class":1935},"'seed using listPage fixture'",[1736,47325,829],{"class":1912},[1736,47327,15790],{"class":4866},[1736,47329,17735],{"class":1912},[1736,47331,47332],{"class":5036},"listPage",[1736,47334,7778],{"class":1912},[1736,47336,7013],{"class":4866},[1736,47338,4914],{"class":1912},[1736,47340,47341,47343,47345,47347,47350],{"class":1738,"line":1772},[1736,47342,14497],{"class":4866},[1736,47344,44373],{"class":1918},[1736,47346,4911],{"class":4866},[1736,47348,47349],{"class":1912}," listPage; ",[1736,47351,47352],{"class":6820},"// set the page to the listPage fixture\n",[1736,47354,47355],{"class":1738,"line":1778},[1736,47356,32863],{"class":1912},[1736,47358,47359],{"class":1738,"line":1784},[1736,47360,17657],{"class":1912},[11,47362,47363,47364,47367],{},"If you don't have any setup logic you can leave the seed file empty or add a ",[179,47365,47366],{},"page.goto()"," to a starting URL.",[23,47369,47371],{"id":47370},"planner-agent-creating-a-test-plan","Planner Agent – Creating a Test Plan",[11,47373,47374,47375,47378],{},"The first step is to use the Planner agent to generate a test plan for a specific feature, for example managing the movies list. The plan should be saved in a ",[179,47376,47377],{},"specs"," folder as it is possible you will have multiple plans.",[11,47380,47381],{},"In VS Code open chat mode and select the Playwright Planner Agent. If using the seed file, it should be added to the context before generating the plan.",[138,47383,47385],{"id":47384},"example-prompt","Example Prompt",[299,47387,47390],{"className":47388,"code":47389,"language":304},[302],"Generate a test plan for managing movies list and save as movies-list-plan.md in specs folder\n",[179,47391,47389],{"__ignoreMap":307},[11,47393,47394],{},"The Playwright Planner Agent explores the site, analyzes the \"managing lists\" feature, and produces a structured test plan in markdown format. The generated plan can be reviewed and refined as needed.",[23,47396,47398],{"id":47397},"generator-agent-creating-the-tests","Generator Agent – Creating the Tests",[11,47400,47401],{},"The Playwright Generator agent is used to generate test files from the test plan. As the test plan may include numerous testing scenarios you may want to choose to generate tests for a specific section of the plan.",[11,47403,47404],{},"In VS Code open the chat and select the Playwright Generator Agent.",[138,47406,47385],{"id":47407},"example-prompt-1",[299,47409,47412],{"className":47410,"code":47411,"language":304},[302],"Generate tests for the \"Adding a Movie\" section of the movies-list-plan.md\n",[179,47413,47411],{"__ignoreMap":307},[11,47415,47416],{},"The Playwright Generator Agent navigates through the site and executes each of the scenarios from the chosen section of the test plan.",[11,47418,47419],{},"The result is a set of generated test files, a file for each scenario. If you had any setup logic in the seed file, you will see it will have been copied into each test file.",[11,47421,47422],{},"Next step is to run the generated tests.",[23,47424,47426],{"id":47425},"healer-agent-fixing-a-failing-test","Healer Agent – Fixing a Failing Test",[11,47428,47429],{},"Sometimes the generated tests will all pass, but sometimes there might be some test failures. Instead of debugging manually you can leverage the Playwright Healer agent. Start a new chat and select the Playwright Healer agent, and ask it to help fix the failing test.",[138,47431,47385],{"id":47432},"example-prompt-2",[299,47434,47437],{"className":47435,"code":47436,"language":304},[302],"Run and fix failing tests\n",[179,47438,47436],{"__ignoreMap":307},[11,47440,47441],{},"The Playwright Healer Agent will run the tests in debug mode, check console logs, network requests and the page snapshots to identify the root cause of the failure. It will keep trying to fix the test until it passes or if the agent believes the functionality is broken it marks the test as skipped.",[23,47443,3294],{"id":3293},[11,47445,47446],{},"Playwright Agents enable AI-powered test generation, execution, and healing with minimal manual effort.",[11,47448,47449],{},"To get started, update to the latest version of Playwright (v1.56)",[299,47451,47453],{"className":2665,"code":47452,"language":2667,"meta":307,"style":307},"npm install -D @playwright/test@latest\n",[179,47454,47455],{"__ignoreMap":307},[1736,47456,47457,47459,47461,47463],{"class":1738,"line":1739},[1736,47458,6565],{"class":2674},[1736,47460,4973],{"class":1935},[1736,47462,21717],{"class":1918},[1736,47464,47465],{"class":1935}," @playwright/test@latest\n",[11,47467,47468],{},"Then initialise your agents and choose your preferred agent loop:",[299,47470,47472],{"className":2665,"code":47471,"language":2667,"meta":307,"style":307},"npx playwright agents --loop=vscode/claude/opencode\n",[179,47473,47474],{"__ignoreMap":307},[1736,47475,47476,47478,47480,47483],{"class":1738,"line":1739},[1736,47477,2675],{"class":2674},[1736,47479,22593],{"class":1935},[1736,47481,47482],{"class":1935}," agents",[1736,47484,47485],{"class":1918}," --loop=vscode/claude/opencode\n",[11,47487,47488],{},"With Planner, Generator, and Healer working together, maintaining end-to-end tests becomes faster, smarter, and more reliable.",[11,47490,47491],{},"Happy testing with AI and Playwright Agents!",[2011,47493,47494],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":307,"searchDepth":748,"depth":748,"links":47496},[47497,47498,47499,47502,47505,47508],{"id":47213,"depth":748,"text":47214},{"id":47244,"depth":748,"text":47245},{"id":47370,"depth":748,"text":47371,"children":47500},[47501],{"id":47384,"depth":756,"text":47385},{"id":47397,"depth":748,"text":47398,"children":47503},[47504],{"id":47407,"depth":756,"text":47385},{"id":47425,"depth":748,"text":47426,"children":47506},[47507],{"id":47432,"depth":756,"text":47385},{"id":3293,"depth":748,"text":3294},"https://dev.to/playwright/playwright-agents-planner-generator-and-healer-in-action-5ajh","2024-10-06","Playwright has introduced a powerful new feature: Playwright Agents. These agents can generate test plans, create tests based on those plans, and even debug and fix failing tests automatically.",{},"/blog/playwright-agents-planner-generator-and-healer-in-action",{"title":47202,"description":47511},"blog/playwright-agents-planner-generator-and-healer-in-action",[1412,1411,795],"Z_FXdVB8J18JJZYn1naJ5G5hXDKHKGDM7ZhG2k8pHbQ",{"id":47519,"title":47520,"body":47521,"canonical":788,"date":47525,"description":47526,"extension":786,"featured":787,"image":47527,"meta":47528,"navigation":790,"ogimage":788,"path":47529,"provider":3460,"published":787,"seo":47530,"stem":47531,"tags":47532,"url":47533,"__hash__":47534},"blog/blog/playwright-global-setup.md","A better global setup in Playwright reusing login with project dependencies",{"type":8,"value":47522,"toc":47523},[],{"title":307,"searchDepth":748,"depth":748,"links":47524},[],"2023-03-15","When you use global setup, you don't see a trace for the setup part of your tests and the setup doesn't appear in the HTML report. This can make debugging difficult. It's also not possible to use fixtures in global setup. In order to fix this issue, project dependencies were created.","v1630862642/debbie.codes/featured-posts/setup_bznutp",{"platform":5508},"/blog/playwright-global-setup",{"title":47520,"description":47526},"blog/playwright-global-setup",[1412,1411],"https://dev.to/playwright/a-better-global-setup-in-playwright-reusing-login-with-project-dependencies-14","WPC7vxv7ndGkO1ol-xFVY926NoC-HHLHyZpXWsH0GWw",{"id":47536,"title":47537,"body":47538,"canonical":788,"date":47542,"description":47543,"extension":786,"featured":787,"image":47544,"meta":47545,"navigation":790,"ogimage":788,"path":47546,"provider":3460,"published":787,"seo":47547,"stem":47548,"tags":47549,"url":47550,"__hash__":47551},"blog/blog/playwright-local-dev-server.md","Setup a local dev server for your Playwright tests",{"type":8,"value":47539,"toc":47540},[],{"title":307,"searchDepth":748,"depth":748,"links":47541},[],"2023-03-07","Playwright comes with a webserver option in the config file which gives you the ability to launch a local dev server before running your tests. This is ideal for when writing your tests during development and when you don't have a staging or production url to test against.","v1630862642/debbie.codes/featured-posts/local-dev-server_z2dylp",{"platform":5508},"/blog/playwright-local-dev-server",{"title":47537,"description":47543},"blog/playwright-local-dev-server",[1412,1411],"https://dev.to/playwright/setup-a-local-dev-server-for-your-playwright-tests-33m9","RnuS_Ez7T6PKTL963ciSDex5OIqYV6kP1-AeyHMiToE",{"id":47553,"title":47554,"body":47555,"canonical":47671,"date":47672,"description":47673,"extension":786,"featured":787,"image":788,"meta":47674,"navigation":790,"ogimage":788,"path":47675,"provider":788,"published":790,"seo":47676,"stem":47677,"tags":47678,"url":788,"__hash__":47679},"blog/blog/playwright-mcp-servers-explained-automation-and-testing.md","Playwright MCP Servers Explained Automation and Testing",{"type":8,"value":47556,"toc":47662},[47557,47560,47563,47567,47571,47574,47588,47591,47595,47598,47612,47614,47617,47631,47634,47636,47639,47642,47644,47659],[11,47558,47559],{},"Did you know Playwright has two MCP servers? Yes, kinda confusing, let me explain it. The Playwright MCP server is great for Browser Automation, filling out forms for example or even using so LLMs can verify their work by opening the browser and taking a page snapshot to see it actually implemented what it said it did. It is built into GitHub Copilot Coding Agent so if you assign a PR to Copilot it will use Playwright which you can see in the session logs. It is very cool indeed.",[11,47561,47562],{},"Then we have another Playwright MCP server called Playwright Test MCP which is built into Playwright test and is for, yes you guessed it, testing. It has some similar tools as the Playwright MCP server but it also has other ones that you only need if you are testing. It starts running when you use the Playwright Agents, Planner, Generator and Healer. However this MCP server only supports TypeScript/JavaScript for now.",[23,47564,47566],{"id":47565},"the-two-different-mcp-servers","The Two Different MCP Servers",[138,47568,47570],{"id":47569},"playwright-mcp-server","Playwright MCP Server",[11,47572,47573],{},"The Playwright MCP server is designed for general browser automation tasks. It's perfect for:",[70,47575,47576,47579,47582,47585],{},[73,47577,47578],{},"Filling out forms automatically",[73,47580,47581],{},"Browser automation workflows",[73,47583,47584],{},"Allowing LLMs to verify their work by taking page snapshots",[73,47586,47587],{},"Integration with GitHub Copilot Coding Agent",[11,47589,47590],{},"When you assign a pull request to GitHub Copilot, it will automatically use the Playwright MCP server behind the scenes, which you can see in the session logs.",[138,47592,47594],{"id":47593},"playwright-test-mcp-server","Playwright Test MCP Server",[11,47596,47597],{},"The Playwright Test MCP server is specifically built for testing scenarios. It includes:",[70,47599,47600,47603,47606,47609],{},[73,47601,47602],{},"Similar tools to the regular Playwright MCP server",[73,47604,47605],{},"Additional testing-specific tools and capabilities",[73,47607,47608],{},"Integration with Playwright Agents (Planner, Generator, and Healer)",[73,47610,47611],{},"Currently supports TypeScript/JavaScript only",[23,47613,15435],{"id":15434},[11,47615,47616],{},"So depending on your needs you can use one MCP server or the other. The Playwright MCP server you need to install while the Playwright Test MCP server is installed when you run an npx command when using the latest version of Playwright.",[299,47618,47619],{"className":2665,"code":47224,"language":2667,"meta":307,"style":307},[179,47620,47621],{"__ignoreMap":307},[1736,47622,47623,47625,47627,47629],{"class":1738,"line":1739},[1736,47624,2675],{"class":2674},[1736,47626,22593],{"class":1935},[1736,47628,47235],{"class":1935},[1736,47630,47238],{"class":1918},[11,47632,47633],{},"The installing of the MCP server is done for you and it doesn't matter what other MCP server you have as the agent will only use the tools that it has assigned to it.",[23,47635,3294],{"id":3293},[11,47637,47638],{},"Both MCP servers serve different purposes but complement each other well in a comprehensive testing and automation workflow. Whether you're doing browser automation or writing tests, there's a Playwright MCP server designed for your specific needs.",[11,47640,47641],{},"Check out the docs for more info on how to get started. Have fun and happy testing with Playwright MCPs!",[23,47643,5706],{"id":5705},[70,47645,47646,47653],{},[73,47647,47648],{},[15,47649,47652],{"href":47650,"rel":47651},"https://playwright.dev/docs/test-agents",[19],"Playwright Test Agents Documentation",[73,47654,47655],{},[15,47656,47658],{"href":5582,"rel":47657},[19],"Playwright MCP GitHub Repository",[2011,47660,47661],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":47663},[47664,47668,47669,47670],{"id":47565,"depth":748,"text":47566,"children":47665},[47666,47667],{"id":47569,"depth":756,"text":47570},{"id":47593,"depth":756,"text":47594},{"id":15434,"depth":748,"text":15435},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"https://dev.to/debs_obrien/playwright-mcp-servers-explained-automation-and-testing-4mo0","2024-11-17","Did you know Playwright has two MCP servers? Learn about the Playwright MCP server for browser automation and the Playwright Test MCP server for testing, both designed to work with AI agents and GitHub Copilot.",{},"/blog/playwright-mcp-servers-explained-automation-and-testing",{"title":47554,"description":47673},"blog/playwright-mcp-servers-explained-automation-and-testing",[1412,3321,795,1411],"L5MRQLR9i2bpjvbnDZ6K5E1ugON4GcTZumhim5tH-fE",{"id":47681,"title":47682,"body":47683,"canonical":788,"date":47861,"description":47862,"extension":786,"featured":787,"image":47863,"meta":47864,"navigation":790,"ogimage":788,"path":47866,"provider":3460,"published":787,"seo":47867,"stem":47868,"tags":47869,"url":788,"__hash__":47870},"blog/blog/progressive-web-apps.md","How Progressive Web Apps work",{"type":8,"value":47684,"toc":47852},[47685,47688,47697,47701,47704,47708,47717,47723,47727,47730,47736,47740,47743,47749,47753,47756,47762,47766,47769,47780,47786,47797,47833,47835,47849],[11,47686,47687],{},"Progressive web apps can hugely improve your performance. If you're internet goes down, you and your customers will still see your site instead of the Downasaur that Chrome shows you when you have no internet connection.",[11,47689,47690,47691,47696],{},"To see how it works open the ",[15,47692,47695],{"href":47693,"rel":47694},"https://nuxtjs.org/",[19],"Nuxt website"," and turn off your internet and you will see that you can still browse some of the pages. Of course not all of them, as to cache all the pages of the site would have a negative effect on performance. Anything that has previously been visited will be cached as well as the pages that have been prefetched.",[23,47698,47700],{"id":47699},"the-service-workers","The Service Workers",[11,47702,47703],{},"PWAs are made possible by the use of Service Workers that are working in the background of your application. Their job is to store what is needed for the first-time load of the app. This is done by caching some static assets which can of course speed up the performance of your web whether you are connected to the internet or not.",[23,47705,47707],{"id":47706},"the-manifest-file","The Manifest File",[11,47709,47710,47711,47716],{},"The manifest file is a config file that contains your applications information such as the icon to be displayed, it's name, theme color and so much more. The ",[15,47712,47715],{"href":47713,"rel":47714},"https://pwa.nuxtjs.org/",[19],"Nuxt PWA module"," will set all this up for you and you can customize it further if you want to. You can easily see what has been setup by opening the Application tab of the Chrome Dev Tools.",[11,47718,47719],{},[121,47720],{"alt":47721,"src":47722},"Manifest file","https://res.cloudinary.com/debsobrien/image/upload/f_auto,fl_lossy,q_auto/v1612442945/debbie.codes/blog/manifest-json_px311e.png",[23,47724,47726],{"id":47725},"the-icons","The Icons",[11,47728,47729],{},"On of the requirements of a PWA is to have an icon which must be 512x512 pixels in size. This should be in the static folder of your Nuxt application. The icon is what will be shown to the user when they download your app to their mobile or desktop. Nuxt will automatically create the different sizes needed for the different devices. Again you can see this in the Application tab in the Chrome Dev Tools.",[11,47731,47732],{},[121,47733],{"alt":47734,"src":47735},"pwa icons","https://res.cloudinary.com/debsobrien/image/upload/f_auto,fl_lossy,q_auto/v1612443336/debbie.codes/blog/pwa-icons_f28jtg.png",[23,47737,47739],{"id":47738},"installing-your-app","Installing your app",[11,47741,47742],{},"When browsing your apps website if a manifest file is found Chrome will trigger the web app install button so the user can install your application should they want to.",[11,47744,47745],{},[121,47746],{"alt":47747,"src":47748},"install app","https://res.cloudinary.com/debsobrien/image/upload/v1612443662/debbie.codes/blog/install-app_z3nqdh.png",[23,47750,47752],{"id":47751},"using-the-app","Using the app",[11,47754,47755],{},"Once you have installed the application you can save it in the dock or even ask Siri to open your application for you, making it easer to find than opening the website in the browser. Once you close the app and open it again new content will be fetched so the PWA is always up to date.",[11,47757,47758],{},[121,47759],{"alt":47760,"src":47761},"PWA installed","https://res.cloudinary.com/debsobrien/image/upload/f_auto,fl_lossy,q_auto/v1612443512/debbie.codes/blog/pwa-installed_kw8gym.png",[23,47763,47765],{"id":47764},"lighthouse-scores","Lighthouse Scores",[11,47767,47768],{},"And of course make sure you check your Lighthouse Score, which has changed in Lighthouse 7 (available in Chrome Canary), and now only includes 2 categories, Installable and PWA Optimised. Previously in Lighthouse 6 there were 3 categories.",[11,47770,47771,47772,47775,47776,47779],{},"The Fast and reliable category has now been removed due to the revamped installability requirements which now includes ",[179,47773,47774],{},"offline-capability"," checking and there are some other changes such as ",[179,47777,47778],{},"load-fast-enough-for-pwa"," audit which has been removed since Lighthouse's existing performance metrics more than cover the needs.",[11,47781,47782],{},[121,47783],{"alt":47784,"src":47785},"lighthouse pwa score","https://res.cloudinary.com/debsobrien/image/upload/f_auto,fl_lossy,q_auto/v1612444177/debbie.codes/blog/lighthouse_cwbemf.png",[11,47787,47788,47789,47792,47793,47796],{},"At the time of writing the only thing I had to add to the Nuxt PWA module to get full score was the theme colour as the default is ",[179,47790,47791],{},"undefined"," and Lighthouse requires it to be set. The theme colour can be set by adding it to the ",[179,47794,47795],{},"pwa.manifest.theme_color"," in your Nuxt config file.",[299,47798,47800],{"className":8734,"code":47799,"filename":33252,"language":8736,"meta":307,"style":307},"pwa: {\n    manifest: {\n      theme_color: '#091a28'\n    }\n  },\n",[179,47801,47802,47808,47815,47825,47829],{"__ignoreMap":307},[1736,47803,47804,47806],{"class":1738,"line":1739},[1736,47805,37411],{"class":2674},[1736,47807,1922],{"class":1912},[1736,47809,47810,47813],{"class":1738,"line":748},[1736,47811,47812],{"class":2674},"    manifest",[1736,47814,1922],{"class":1912},[1736,47816,47817,47820,47822],{"class":1738,"line":756},[1736,47818,47819],{"class":2674},"      theme_color",[1736,47821,3065],{"class":1912},[1736,47823,47824],{"class":1935},"'#091a28'\n",[1736,47826,47827],{"class":1738,"line":1755},[1736,47828,9853],{"class":1912},[1736,47830,47831],{"class":1738,"line":1761},[1736,47832,4929],{"class":1912},[23,47834,5706],{"id":5705},[70,47836,47837,47842],{},[73,47838,47839],{},[15,47840,47715],{"href":47713,"rel":47841},[19],[73,47843,47844],{},[15,47845,47848],{"href":47846,"rel":47847},"https://github.com/GoogleChrome/lighthouse/releases",[19],"Lighthouse 7 release notes",[2011,47850,47851],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":47853},[47854,47855,47856,47857,47858,47859,47860],{"id":47699,"depth":748,"text":47700},{"id":47706,"depth":748,"text":47707},{"id":47725,"depth":748,"text":47726},{"id":47738,"depth":748,"text":47739},{"id":47751,"depth":748,"text":47752},{"id":47764,"depth":748,"text":47765},{"id":5705,"depth":748,"text":5706},"2021-02-04","Progressive web apps will really help your performance and you can install them on your home screen or desktop and have that app like feel for your website. Let's have a look at how they work.","v1612443662/debbie.codes/blog/install-app_z3nqdh",{"video":47865},"3RWBkPdKtBQ","/blog/progressive-web-apps",{"title":47682,"description":47862},"blog/progressive-web-apps",[5239,36709],"Sh28hnoi0aPrPCe7PWrYoKVMUMvNBAyDJmhPxRQ1uOE",{"id":47872,"title":47873,"body":47874,"canonical":788,"date":47878,"description":47879,"extension":786,"featured":787,"image":47880,"meta":47881,"navigation":790,"ogimage":788,"path":47883,"provider":5235,"published":787,"seo":47884,"stem":47885,"tags":47886,"url":47887,"__hash__":47888},"blog/blog/publish-multiple-components-to-npm.md","Publish Multiple Components to NPM with no package.jsons to Maintain",{"type":8,"value":47875,"toc":47876},[],{"title":307,"searchDepth":748,"depth":748,"links":47877},[],"2021-08-11","Sharing components across multiple projects or with other developers seems to be the most complicated thing, yet it really shouldn’t be. With package registries such as npm or GitHub packages surely we should easily be able to share our components by publishing them as packages.","photo-1543966888-7c1dc482a810?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80",{"loading":3458,"platform":47882},"Bits n Pieces","/blog/publish-multiple-components-to-npm",{"title":47873,"description":47879},"blog/publish-multiple-components-to-npm",[27530],"https://blog.bitsrc.io/publish-multiple-components-to-npm-with-no-package-jsons-to-maintain-db34f0fbf5aa","Q9Smyv0RPimUm-JlUxv4YzgtWAMvwA5cg6s4Bo2XevQ",{"id":47890,"title":47891,"body":47892,"canonical":788,"date":48309,"description":48310,"extension":786,"featured":787,"image":48311,"meta":48312,"navigation":790,"ogimage":788,"path":48313,"provider":5235,"published":790,"seo":48314,"stem":48315,"tags":48316,"url":788,"__hash__":48317},"blog/blog/python-and-playwright.md","Getting Started with Python and Playwright",{"type":8,"value":47893,"toc":48301},[47894,47903,47906,47919,47923,47948,47954,47968,47975,47989,47992,48004,48008,48019,48033,48036,48054,48062,48074,48077,48080,48092,48094,48103,48107,48124,48138,48141,48152,48156,48165,48250,48254,48257,48266,48273,48284,48287,48289,48298],[11,47895,47896,47897,47902],{},"This is a quick guide on how to setup Python and Pytest and install Playwright and get your first test up and running. As a very new user to Python there were a couple of things I had to do to first get Python installed on the computer. This will depend of course on the operating system you are using. Check out the ",[15,47898,47901],{"href":47899,"rel":47900},"https://www.python.org/downloads/",[19],"Python website"," for more information on how to install Python.",[11,47904,47905],{},"Once Python has been installed on your computer you can check it has been properly installed by running the following command in your terminal:",[299,47907,47909],{"className":2665,"code":47908,"language":2667,"meta":307,"style":307},"python3 --version\n",[179,47910,47911],{"__ignoreMap":307},[1736,47912,47913,47916],{"class":1738,"line":1739},[1736,47914,47915],{"class":2674},"python3",[1736,47917,47918],{"class":1918}," --version\n",[23,47920,47922],{"id":47921},"manage-multiple-versions-of-python","Manage multiple versions of Python",[11,47924,47925,47926,47929,47930,47935,47936,47943,47944,47947],{},"We then use ",[179,47927,47928],{},"pyenv"," which is a command line tool used to manage multiple versions of Python. This is useful if you are working on multiple projects that use different versions of Python. Check out the ",[15,47931,47934],{"href":47932,"rel":47933},"https://github.com/pyenv/pyenv",[19],"GitHub readme"," of the 'pyenv' project for more information on how to install it on your operating system. For windows check out ",[15,47937,47940],{"href":47938,"rel":47939},"https://github.com/pyenv-win/pyenv-win",[19],[179,47941,47942],{},"pyenv-win"," or you can use ",[179,47945,47946],{},"venv"," Python's Built-in Virtual Environment.",[11,47949,47950,47951,47953],{},"Once ",[179,47952,47928],{}," has been installed we can use the following command to install a specific version of Python:",[299,47955,47957],{"className":2665,"code":47956,"language":2667,"meta":307,"style":307},"pyenv install 3.11\n",[179,47958,47959],{"__ignoreMap":307},[1736,47960,47961,47963,47965],{"class":1738,"line":1739},[1736,47962,47928],{"class":2674},[1736,47964,4973],{"class":1935},[1736,47966,47967],{"class":1918}," 3.11\n",[11,47969,47970,47971,47974],{},"This command will download the course code for Python 3.11, compile it and set it up as one of the available versions of Python that you can use on your computer. We can then use the ",[179,47972,47973],{},"pyenv shell"," command to switch to and use the version of Python we just installed:",[299,47976,47978],{"className":2665,"code":47977,"language":2667,"meta":307,"style":307},"pyenv shell 3.11\n",[179,47979,47980],{"__ignoreMap":307},[1736,47981,47982,47984,47987],{"class":1738,"line":1739},[1736,47983,47928],{"class":2674},[1736,47985,47986],{"class":1935}," shell",[1736,47988,47967],{"class":1918},[11,47990,47991],{},"In order to check you are using the correct version of Python you can run the following command:",[299,47993,47995],{"className":2665,"code":47994,"language":2667,"meta":307,"style":307},"python --version\n",[179,47996,47997],{"__ignoreMap":307},[1736,47998,47999,48002],{"class":1738,"line":1739},[1736,48000,48001],{"class":2674},"python",[1736,48003,47918],{"class":1918},[23,48005,48007],{"id":48006},"create-a-virtual-environment","Create a virtual environment",[11,48009,48010,48011,48014,48015,48018],{},"Next we need to download a ",[179,48012,48013],{},"virtualenv"," package from the Python Package Index (PyPI). This is a tool used to create isolated Python environments. Virtual environments allow you to create a self-contained environment with its own set of Python libraries and dependencies and prevents conflicts between different project dependencies when working on multiple projects. The package installer for Python is called ",[179,48016,48017],{},"pip",". In order to install our virtual environment we can run the following command:",[299,48020,48022],{"className":2665,"code":48021,"language":2667,"meta":307,"style":307},"pip install virtualenv\n",[179,48023,48024],{"__ignoreMap":307},[1736,48025,48026,48028,48030],{"class":1738,"line":1739},[1736,48027,48017],{"class":2674},[1736,48029,4973],{"class":1935},[1736,48031,48032],{"class":1935}," virtualenv\n",[11,48034,48035],{},"We can then create a new folder for our project and change directory so we are inside the folder. We can do this using the terminal with the following commands:",[299,48037,48039],{"className":2665,"code":48038,"language":2667,"meta":307,"style":307},"mkdir my-project\ncd my-project\n",[179,48040,48041,48048],{"__ignoreMap":307},[1736,48042,48043,48045],{"class":1738,"line":1739},[1736,48044,15487],{"class":2674},[1736,48046,48047],{"class":1935}," my-project\n",[1736,48049,48050,48052],{"class":1738,"line":748},[1736,48051,15495],{"class":1918},[1736,48053,48047],{"class":1935},[11,48055,48056,48057,48059,48060,1087],{},"Now we need to create a virtual environment for our project. Inside the directory we have just created we will create an isolated environment with its own Python interpreter and a separate set of Python libraries and packages. ",[179,48058,48013],{}," is the command used to create a virtual environment and we can specify the name of the virtual environment we want to create. In this case we will call it ",[179,48061,39573],{},[299,48063,48065],{"className":2665,"code":48064,"language":2667,"meta":307,"style":307},"virtualenv env\n",[179,48066,48067],{"__ignoreMap":307},[1736,48068,48069,48071],{"class":1738,"line":1739},[1736,48070,48013],{"class":2674},[1736,48072,48073],{"class":1935}," env\n",[11,48075,48076],{},"Once the virtual environment has been created we can activate it using the following command:",[11,48078,48079],{},"On MacOS and Linux:",[299,48081,48083],{"className":2665,"code":48082,"language":2667,"meta":307,"style":307},"source env/bin/activate\n",[179,48084,48085],{"__ignoreMap":307},[1736,48086,48087,48089],{"class":1738,"line":1739},[1736,48088,5304],{"class":1918},[1736,48090,48091],{"class":1935}," env/bin/activate\n",[11,48093,17019],{},[299,48095,48097],{"className":2665,"code":48096,"language":2667,"meta":307,"style":307},"env\\Scripts\\activate\n",[179,48098,48099],{"__ignoreMap":307},[1736,48100,48101],{"class":1738,"line":1739},[1736,48102,48096],{"class":2674},[23,48104,48106],{"id":48105},"install-pytest-and-playwright","Install Pytest and Playwright",[11,48108,48109,48110,48113,48114,48116,48117,48119,48120,48123],{},"Now that everything as been setup we can go ahead and start installing our ",[179,48111,48112],{},"pytest-playwright"," package, which is a plugin that integrates Playwright with the Pytest testing framework. The package installer for Python is called ",[179,48115,48017],{}," which is used to install packages from the Python Package Index (PyPI). We can install the ",[179,48118,48112],{}," package using the following command which will also install ",[179,48121,48122],{},"pytest"," if you haven't installed it already.",[299,48125,48127],{"className":2665,"code":48126,"language":2667,"meta":307,"style":307},"pip install pytest-playwright\n",[179,48128,48129],{"__ignoreMap":307},[1736,48130,48131,48133,48135],{"class":1738,"line":1739},[1736,48132,48017],{"class":2674},[1736,48134,4973],{"class":1935},[1736,48136,48137],{"class":1935}," pytest-playwright\n",[11,48139,48140],{},"Next we need to use the command-line tool from Playwright to install the required browser binaries, which are stored locally and used by Playwright to launch and interact with the browsers. These can be installed with the following command:",[299,48142,48144],{"className":2665,"code":48143,"language":2667,"meta":307,"style":307},"playwright install\n",[179,48145,48146],{"__ignoreMap":307},[1736,48147,48148,48150],{"class":1738,"line":1739},[1736,48149,1412],{"class":2674},[1736,48151,33071],{"class":1935},[23,48153,48155],{"id":48154},"create-a-test","Create a test",[11,48157,48158,48159,48162,48163,891],{},"Now in our editor of choice we can create a new file called ",[179,48160,48161],{},"test_example.py"," and create an example test. In Python we need to always prefix our test file with the word ",[179,48164,25293],{},[299,48166,48169],{"className":48167,"code":48168,"language":48001,"meta":307,"style":307},"language-python shiki shiki-themes github-light github-dark","import re\nfrom playwright.sync_api import Page, expect\n\ndef test_has_title(page: Page):\n    page.goto(\"https://playwright.dev/\")\n\n    # Expect a title \"to contain\" a substring.\n    expect(page).to_have_title(re.compile(\"Playwright\"))\n\ndef test_get_started_link(page: Page):\n    page.goto(\"https://playwright.dev/\")\n\n    # Click the get started link.\n    page.get_by_role(\"link\", name=\"Get started\").click()\n\n    # Expects page to have a heading with the name of Installation.\n    expect(page.get_by_role(\"heading\", name=\"Installation\")).to_be_visible()\n",[179,48170,48171,48176,48181,48185,48190,48195,48199,48204,48209,48213,48218,48222,48226,48231,48236,48240,48245],{"__ignoreMap":307},[1736,48172,48173],{"class":1738,"line":1739},[1736,48174,48175],{},"import re\n",[1736,48177,48178],{"class":1738,"line":748},[1736,48179,48180],{},"from playwright.sync_api import Page, expect\n",[1736,48182,48183],{"class":1738,"line":756},[1736,48184,1747],{"emptyLinePlaceholder":790},[1736,48186,48187],{"class":1738,"line":1755},[1736,48188,48189],{},"def test_has_title(page: Page):\n",[1736,48191,48192],{"class":1738,"line":1761},[1736,48193,48194],{},"    page.goto(\"https://playwright.dev/\")\n",[1736,48196,48197],{"class":1738,"line":1767},[1736,48198,1747],{"emptyLinePlaceholder":790},[1736,48200,48201],{"class":1738,"line":1772},[1736,48202,48203],{},"    # Expect a title \"to contain\" a substring.\n",[1736,48205,48206],{"class":1738,"line":1778},[1736,48207,48208],{},"    expect(page).to_have_title(re.compile(\"Playwright\"))\n",[1736,48210,48211],{"class":1738,"line":1784},[1736,48212,1747],{"emptyLinePlaceholder":790},[1736,48214,48215],{"class":1738,"line":1790},[1736,48216,48217],{},"def test_get_started_link(page: Page):\n",[1736,48219,48220],{"class":1738,"line":1796},[1736,48221,48194],{},[1736,48223,48224],{"class":1738,"line":2353},[1736,48225,1747],{"emptyLinePlaceholder":790},[1736,48227,48228],{"class":1738,"line":2358},[1736,48229,48230],{},"    # Click the get started link.\n",[1736,48232,48233],{"class":1738,"line":2364},[1736,48234,48235],{},"    page.get_by_role(\"link\", name=\"Get started\").click()\n",[1736,48237,48238],{"class":1738,"line":2370},[1736,48239,1747],{"emptyLinePlaceholder":790},[1736,48241,48242],{"class":1738,"line":2376},[1736,48243,48244],{},"    # Expects page to have a heading with the name of Installation.\n",[1736,48246,48247],{"class":1738,"line":2381},[1736,48248,48249],{},"    expect(page.get_by_role(\"heading\", name=\"Installation\")).to_be_visible()\n",[23,48251,48253],{"id":48252},"run-our-test-with-playwright","Run our test with Playwright",[11,48255,48256],{},"We are now ready to run our test to see if it works. We can do this by running the following command in the terminal:",[299,48258,48260],{"className":2665,"code":48259,"language":2667,"meta":307,"style":307},"pytest\n",[179,48261,48262],{"__ignoreMap":307},[1736,48263,48264],{"class":1738,"line":1739},[1736,48265,48259],{"class":2674},[11,48267,48268,48269,48272],{},"And right there in the terminal you can see we now have a passing test. We have successfully setup Python and Pytest and installed Playwright and got our first test up and running. However we did not visually see our test pass as by default Playwright runs in headless mode meaning without a browser window. We can change this by passing the ",[179,48270,48271],{},"--headed"," flag to our command:",[299,48274,48276],{"className":2665,"code":48275,"language":2667,"meta":307,"style":307},"pytest --headed\n",[179,48277,48278],{"__ignoreMap":307},[1736,48279,48280,48282],{"class":1738,"line":1739},[1736,48281,48122],{"class":2674},[1736,48283,22616],{"class":1918},[11,48285,48286],{},"This time you will see a browser window pop up and the test will run so you can visually see it pass.",[23,48288,3294],{"id":3293},[11,48290,48291,48292,48297],{},"In this post we learnt how to setup Python and install Playwright so we can easily write and run our Playwright tests in Python. Check out the ",[15,48293,48296],{"href":48294,"rel":48295},"https://playwright.dev/python/docs/intro",[19],"Playwright documentation"," for more information on writing, running and generating Playwright tests in Python.",[2011,48299,48300],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":307,"searchDepth":748,"depth":748,"links":48302},[48303,48304,48305,48306,48307,48308],{"id":47921,"depth":748,"text":47922},{"id":48006,"depth":748,"text":48007},{"id":48105,"depth":748,"text":48106},{"id":48154,"depth":748,"text":48155},{"id":48252,"depth":748,"text":48253},{"id":3293,"depth":748,"text":3294},"2023-09-07","For those new to Python here is a quick beginners guide on how to setup Python and Pytest and install Playwright. We then create an example test and run it in both headed and headless mode meaning with and without a browser window.","photo-1528158222524-d4d912d2e208?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=800&q=80",{},"/blog/python-and-playwright",{"title":47891,"description":48310},"blog/python-and-playwright",[1412,1411],"swTZAmtBe4rET8r_-RQCM-kP2NOMaTXDB4web_Xg9NU",{"id":48319,"title":48320,"body":48321,"canonical":788,"date":22768,"description":48325,"extension":786,"featured":787,"image":48589,"meta":48590,"navigation":790,"ogimage":788,"path":48592,"provider":3460,"published":790,"seo":48593,"stem":48594,"tags":48595,"url":788,"__hash__":48596},"blog/blog/reports-tracing-tests-in-playwright.md","Playwright Reports and Traces",{"type":8,"value":48322,"toc":48578},[48323,48326,48331,48335,48338,48352,48355,48361,48365,48368,48372,48378,48392,48395,48454,48458,48465,48477,48480,48486,48490,48493,48499,48502,48505,48508,48511,48514,48522,48526,48529,48537,48539,48542,48545,48547,48575],[11,48324,48325],{},"Playwright will serve up a HTML report on your local server so you can easily walk through the steps of your test. You can also open a trace file which gives you even more powerful options of viewing, reporting, interacting and even debugging your tests.",[11,48327,22542,48328,22548],{},[15,48329,22547],{"href":22545,"rel":48330},[19],[23,48332,48334],{"id":48333},"serving-the-html-report","Serving the Html Report",[11,48336,48337],{},"You can see a full report of your tests by running the following command.",[299,48339,48341],{"className":2665,"code":48340,"language":2667,"meta":307,"style":307},"npx playwright show-report\n",[179,48342,48343],{"__ignoreMap":307},[1736,48344,48345,48347,48349],{"class":1738,"line":1739},[1736,48346,2675],{"class":2674},[1736,48348,22593],{"class":1935},[1736,48350,48351],{"class":1935}," show-report\n",[11,48353,48354],{},"This will open up a browser window with a report of your tests. It will show you what browsers where used to run your tests, how long each test took and it will report each step of the test. The steps are collapsible meaning you can click on it and expand it to see the code ran for that particular part of the test.",[11,48356,48357],{},[121,48358],{"alt":48359,"src":48360},"test report showing list of steps test made","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648215659/debbie.codes/blog/2022/test-report_dio73s.png",[23,48362,48364],{"id":48363},"trace-viewer","Trace Viewer",[11,48366,48367],{},"This is one of my favourite features of Playwright. It allows you to view the trace file of your test and interact with it.",[138,48369,48371],{"id":48370},"setting-the-trace-in-playwright-config","Setting the Trace in Playwright Config",[11,48373,48374,48375,48377],{},"To run the trace file you first need to modify the ",[179,48376,25689],{}," file. There are a few options listed below.",[70,48379,48380,48383,48386,48389],{},[73,48381,48382],{},"'off' - Do not record a trace",[73,48384,48385],{},"'on' - Record a trace for each test",[73,48387,48388],{},"'retain-on-failure' - Record a trace for each test, but remove it from successful test runs",[73,48390,48391],{},"'on-first-retry' - Record a trace only when retrying a test for the first time",[11,48393,48394],{},"For this example I have set the trace to 'on' so that I always get trace files for my tests.",[299,48396,48398],{"className":4894,"code":48397,"language":4896,"meta":307,"style":307},"const config: PlaywrightTestConfig = {\n  ...\n  use: {\n    trace: 'on'\n  }\n  ...\n}\nexport default config\n",[179,48399,48400,48416,48420,48425,48433,48437,48441,48445],{"__ignoreMap":307},[1736,48401,48402,48404,48407,48409,48412,48414],{"class":1738,"line":1739},[1736,48403,5029],{"class":4866},[1736,48405,48406],{"class":1918}," config",[1736,48408,1087],{"class":4866},[1736,48410,48411],{"class":2674}," PlaywrightTestConfig",[1736,48413,4911],{"class":4866},[1736,48415,4914],{"class":1912},[1736,48417,48418],{"class":1738,"line":748},[1736,48419,9048],{"class":4866},[1736,48421,48422],{"class":1738,"line":756},[1736,48423,48424],{"class":1912},"  use: {\n",[1736,48426,48427,48430],{"class":1738,"line":1755},[1736,48428,48429],{"class":1912},"    trace: ",[1736,48431,48432],{"class":1935},"'on'\n",[1736,48434,48435],{"class":1738,"line":1761},[1736,48436,1971],{"class":1912},[1736,48438,48439],{"class":1738,"line":1767},[1736,48440,9048],{"class":4866},[1736,48442,48443],{"class":1738,"line":1772},[1736,48444,1976],{"class":1912},[1736,48446,48447,48449,48451],{"class":1738,"line":1778},[1736,48448,6632],{"class":4866},[1736,48450,30438],{"class":4866},[1736,48452,48453],{"class":1912}," config\n",[138,48455,48457],{"id":48456},"rerunning-the-report","Rerunning the Report",[11,48459,48460,48461,48464],{},"If you still have the html report open you can close it with ",[179,48462,48463],{},"Ctrl+C",". We now need to create a new report which contains the trace file.",[299,48466,48467],{"className":2665,"code":48340,"language":2667,"meta":307,"style":307},[179,48468,48469],{"__ignoreMap":307},[1736,48470,48471,48473,48475],{"class":1738,"line":1739},[1736,48472,2675],{"class":2674},[1736,48474,22593],{"class":1935},[1736,48476,48351],{"class":1935},[11,48478,48479],{},"Just like before the html report will open for you with a report of your test. But now at the very bottom you will see a Trace section with an image and a link to your trace. Clicking this link will open up your trace.",[11,48481,48482],{},[121,48483],{"alt":48484,"src":48485},"report showing trace file zip","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648220070/debbie.codes/blog/2022/trace-viewer-zip_ebyd2q.png",[23,48487,48489],{"id":48488},"viewing-the-trace-file","Viewing the Trace File",[11,48491,48492],{},"Once you have clicked on the trace link you will see a new browser window with your test' trace. This is a locally hosted PWA where you can play around with your test trace.",[11,48494,48495],{},[121,48496],{"alt":48497,"src":48498},"trace viewer in browser window showing all the features it gives you","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648219793/debbie.codes/blog/2022/trace-viewer-broswer_jqoim0.png",[11,48500,48501],{},"At the top is the timeline where you can scroll across and see your page at different states depending on the action. Each action has a different color such as green for clicks and red for selects.",[11,48503,48504],{},"On the left had side is the actions and Metadata where you can see each action of your test. When you click on an action you will then see the outcome of that action in the middle part.",[11,48506,48507],{},"The middle part shows a DOM snapshot of your site with the action highlighted. The before tab shows you what the page was like before the action and the after tab shows you what the page looks like after the action has been clicked.",[11,48509,48510],{},"On the right hand side you can see the call for this action showing what the selector was, if it is in strict mode etc, the console log incase there are any console messages or errors, the network to see all network requests and the source for the tests.",[11,48512,48513],{},"Check out the video to see it in action.",[22564,48515,5302,48516,5302,48519,22574],{"width":6371,"height":6371,"controls":790},[5304,48517],{"src":48518,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648220640/debbie.codes/blog/2022/playwright-trace-viewer_kx8uws.mp4",[5304,48520],{"src":48521,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648220640/debbie.codes/blog/2022/playwright-trace-viewer_kx8uws.ogg",[23,48523,48525],{"id":48524},"inspecting-the-dom-snapshot","Inspecting the DOM Snapshot",[11,48527,48528],{},"The really cool thing about this is that the snapshot of your site in the middle is actually a DOM snapshot meaning it is fully interactive. You can scroll it, click user events and even open the browser dev tools inside this dev tool. Very cool.",[22564,48530,5302,48531,5302,48534,22574],{"width":6371,"height":6371,"controls":790},[5304,48532],{"src":48533,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648220649/debbie.codes/blog/2022/playwright-trace-inspect_wvfjb9.mp4",[5304,48535],{"src":48536,"type":22573},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/v1648220649/debbie.codes/blog/2022/playwright-trace-inspect_wvfjb9.ogg",[23,48538,3294],{"id":3293},[11,48540,48541],{},"The trace viewer is a really cool way of going through your tests and inspecting them further. This is served as a PWA on your local host and therefore is completely safe as all your data is only on your machine.",[11,48543,48544],{},"There is so much more cool things you can do with Playwright and I highly encourage you to check it out especially is it is open source and therefore free to use. Take it for a spin. Have fun and let's get testing!",[23,48546,5706],{"id":5705},[70,48548,48549,48554,48559,48564,48570],{},[73,48550,48551],{},[15,48552,22728],{"href":22726,"rel":48553},[19],[73,48555,48556],{},[15,48557,22735],{"href":22733,"rel":48558},[19],[73,48560,48561],{},[15,48562,22742],{"href":22740,"rel":48563},[19],[73,48565,48566],{},[15,48567,48364],{"href":48568,"rel":48569},"https://playwright.dev/docs/trace-viewer",[19],[73,48571,48572],{},[15,48573,22547],{"href":22545,"rel":48574},[19],[2011,48576,48577],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":307,"searchDepth":748,"depth":748,"links":48579},[48580,48581,48585,48586,48587,48588],{"id":48333,"depth":748,"text":48334},{"id":48363,"depth":748,"text":48364,"children":48582},[48583,48584],{"id":48370,"depth":756,"text":48371},{"id":48456,"depth":756,"text":48457},{"id":48488,"depth":748,"text":48489},{"id":48524,"depth":748,"text":48525},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"v1648219793/debbie.codes/blog/2022/trace-viewer-broswer_jqoim0.png",{"ogImage":48591,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648219793/debbie.codes/blog/2022/trace-viewer-broswer_jqoim0.png","/blog/reports-tracing-tests-in-playwright",{"title":48320,"description":48325},"blog/reports-tracing-tests-in-playwright",[1411,1412],"W7ch7VdY4wH7nTZrpQKRCiZ-vnLQgypCT4nf7wRkv9c",{"id":48598,"title":48599,"body":48600,"canonical":788,"date":48604,"description":48605,"extension":786,"featured":787,"image":48606,"meta":48607,"navigation":790,"ogimage":788,"path":48609,"provider":3460,"published":787,"seo":48610,"stem":48611,"tags":48612,"url":48613,"__hash__":48614},"blog/blog/sharing-react-components-across-multiple-applications.md","Sharing React Components across Multiple Applications",{"type":8,"value":48601,"toc":48602},[],{"title":307,"searchDepth":748,"depth":748,"links":48603},[],"2021-06-16","We have almost all been in a situation where we need to share a component between one app and another. Often we just decide to copy and paste it because it really is the quickest option. And sometimes it’s even ok, especially when it comes to those smaller components. But what if you wanted to share a big and more complex component?","v1630863000/debbie.codes/featured-posts/sharing-react-components_ejjqxp",{"loading":3458,"platform":48608},"Medium","/blog/sharing-react-components-across-multiple-applications",{"title":48599,"description":48605},"blog/sharing-react-components-across-multiple-applications",[5221,27530],"https://blog.bitsrc.io/sharing-react-components-across-multiple-applications-a407b5a15186","f8syjkFIuJ8L-1s7KGivIshgUfq2Nd51zGnCOl97AxE",{"id":48616,"title":48617,"body":48618,"canonical":788,"date":49477,"description":49478,"extension":786,"featured":787,"image":49479,"meta":49480,"navigation":790,"ogimage":788,"path":49481,"provider":3460,"published":790,"seo":49482,"stem":49483,"tags":49484,"url":788,"__hash__":49485},"blog/blog/testing-button-component.md","Testing a Button Component",{"type":8,"value":48619,"toc":49464},[48620,48623,48628,48631,48635,48643,48646,48662,48666,48675,48681,48685,48692,48767,48771,48774,48873,48876,48880,48883,48917,48921,48939,48991,49002,49064,49068,49080,49157,49161,49164,49167,49184,49187,49191,49197,49291,49295,49314,49413,49416,49418,49461],[11,48621,48622],{},"Tests are really important when building shareable components. If someone can't trust your component then they probably won't use it. I have been working on a demo e-commerce project built in React and have spoken about it at many conferences and all the time I keep saying:",[1713,48624,48625],{},[11,48626,48627],{},"You should write tests. Tests are important.",[11,48629,48630],{},"It should be part of the development flow. As you build your components you build your tests. However I am going to be really honest and tell you that I have not been doing this at all. Why? Because like all of you, time. Testing takes time and the demo can be created without tests and no-one will really notice. Not good. I know. But really the honest answer is that I don't really know how to test or what to test. So 2022, it's time to dive deeper into tests and practice what I preach.",[23,48632,48634],{"id":48633},"testing-library","Testing Library",[11,48636,48637,48638,48642],{},"I am building the demo project as React components in Bit and we have support for ",[15,48639,48634],{"href":48640,"rel":48641},"https://testing-library.com/",[19]," which is a fantastic open source library and I really enjoy working with it. I am by no means an expert, but I am going to share with you some of the things I have learnt so far perhaps it might even help you.",[11,48644,48645],{},"Make sure you already have testing library installed.",[299,48647,48649],{"className":2665,"code":48648,"language":2667,"meta":307,"style":307},"npm install --save-dev @testing-library/react\n",[179,48650,48651],{"__ignoreMap":307},[1736,48652,48653,48655,48657,48659],{"class":1738,"line":1739},[1736,48654,6565],{"class":2674},[1736,48656,4973],{"class":1935},[1736,48658,15537],{"class":1918},[1736,48660,48661],{"class":1935}," @testing-library/react\n",[23,48663,48665],{"id":48664},"testing-different-button-compositions","Testing different Button Compositions",[11,48667,48668,48669,48674],{},"I have created a load of different ",[15,48670,48673],{"href":48671,"rel":48672},"https://bit.dev/learn-bit-react/base-ui/ui/button/~compositions",[19],"compositions for my button"," which I will need to test. Compositions allow me to see the button in different states such as primary, secondary, disabled etc.",[11,48676,48677],{},[121,48678],{"alt":48679,"src":48680},"buttons in different states","https://res.cloudinary.com/debsobrien/image/upload/v1641577214/debbie.codes/blog/button-testing_2x_oliu20.png",[138,48682,48684],{"id":48683},"importing-react-testing-library-and-compositions","Importing React, Testing Library and Compositions",[11,48686,48687,48688,48691],{},"In the ",[179,48689,48690],{},"button.spec.tsx"," file we will import the React library and the Testing Library as well as the button compositions that we want to test.",[299,48693,48695],{"className":8734,"code":48694,"language":8736,"meta":307,"style":307},"import React from 'react'\nimport { render, screen } from '@testing-library/react'\nimport {\n  ButtonAsLink,\n  PrimaryButton,\n  PrimaryButtonDisabled,\n  SecondaryButton,\n  SecondaryButtonDisabled,\n  WhiteButton,\n  WhiteButtonDisabled\n} from './button.composition'\n",[179,48696,48697,48707,48717,48723,48728,48733,48738,48743,48748,48753,48758],{"__ignoreMap":307},[1736,48698,48699,48701,48703,48705],{"class":1738,"line":1739},[1736,48700,4996],{"class":4866},[1736,48702,6594],{"class":1912},[1736,48704,5002],{"class":4866},[1736,48706,6599],{"class":1935},[1736,48708,48709,48711,48713,48715],{"class":1738,"line":748},[1736,48710,4996],{"class":4866},[1736,48712,10384],{"class":1912},[1736,48714,5002],{"class":4866},[1736,48716,10389],{"class":1935},[1736,48718,48719,48721],{"class":1738,"line":756},[1736,48720,4996],{"class":4866},[1736,48722,4914],{"class":1912},[1736,48724,48725],{"class":1738,"line":1755},[1736,48726,48727],{"class":1912},"  ButtonAsLink,\n",[1736,48729,48730],{"class":1738,"line":1761},[1736,48731,48732],{"class":1912},"  PrimaryButton,\n",[1736,48734,48735],{"class":1738,"line":1767},[1736,48736,48737],{"class":1912},"  PrimaryButtonDisabled,\n",[1736,48739,48740],{"class":1738,"line":1772},[1736,48741,48742],{"class":1912},"  SecondaryButton,\n",[1736,48744,48745],{"class":1738,"line":1778},[1736,48746,48747],{"class":1912},"  SecondaryButtonDisabled,\n",[1736,48749,48750],{"class":1738,"line":1784},[1736,48751,48752],{"class":1912},"  WhiteButton,\n",[1736,48754,48755],{"class":1738,"line":1790},[1736,48756,48757],{"class":1912},"  WhiteButtonDisabled\n",[1736,48759,48760,48762,48764],{"class":1738,"line":1796},[1736,48761,6871],{"class":1912},[1736,48763,5002],{"class":4866},[1736,48765,48766],{"class":1935}," './button.composition'\n",[138,48768,48770],{"id":48769},"creating-todo-tests","Creating todo tests",[11,48772,48773],{},"We can then start creating our tests. We can first create a list of todos for our tests so we are clear on what we need to test and so we don't forget anything. What we want is to test the button in all it's different states including disabled and if the button is used as a Link.",[299,48775,48777],{"className":4894,"code":48776,"language":4896,"meta":307,"style":307},"it.todo('should render a button with the class of primary')\nit.todo('should render a disabled button with the class of primary')\nit.todo('should render a button with the class of secondary')\nit.todo('should render a disabled button with the class of secondary')\nit.todo('should render a disabled button with the class of white')\nit.todo('should render a disabled button with the class of secondary')\nit.todo(\n  'should render a button as a Link, checks for href attribute and primary class'\n)\n",[179,48778,48779,48792,48805,48818,48831,48844,48856,48864,48869],{"__ignoreMap":307},[1736,48780,48781,48783,48785,48787,48790],{"class":1738,"line":1739},[1736,48782,10426],{"class":1912},[1736,48784,10429],{"class":2674},[1736,48786,7751],{"class":1912},[1736,48788,48789],{"class":1935},"'should render a button with the class of primary'",[1736,48791,7045],{"class":1912},[1736,48793,48794,48796,48798,48800,48803],{"class":1738,"line":748},[1736,48795,10426],{"class":1912},[1736,48797,10429],{"class":2674},[1736,48799,7751],{"class":1912},[1736,48801,48802],{"class":1935},"'should render a disabled button with the class of primary'",[1736,48804,7045],{"class":1912},[1736,48806,48807,48809,48811,48813,48816],{"class":1738,"line":756},[1736,48808,10426],{"class":1912},[1736,48810,10429],{"class":2674},[1736,48812,7751],{"class":1912},[1736,48814,48815],{"class":1935},"'should render a button with the class of secondary'",[1736,48817,7045],{"class":1912},[1736,48819,48820,48822,48824,48826,48829],{"class":1738,"line":1755},[1736,48821,10426],{"class":1912},[1736,48823,10429],{"class":2674},[1736,48825,7751],{"class":1912},[1736,48827,48828],{"class":1935},"'should render a disabled button with the class of secondary'",[1736,48830,7045],{"class":1912},[1736,48832,48833,48835,48837,48839,48842],{"class":1738,"line":1761},[1736,48834,10426],{"class":1912},[1736,48836,10429],{"class":2674},[1736,48838,7751],{"class":1912},[1736,48840,48841],{"class":1935},"'should render a disabled button with the class of white'",[1736,48843,7045],{"class":1912},[1736,48845,48846,48848,48850,48852,48854],{"class":1738,"line":1767},[1736,48847,10426],{"class":1912},[1736,48849,10429],{"class":2674},[1736,48851,7751],{"class":1912},[1736,48853,48828],{"class":1935},[1736,48855,7045],{"class":1912},[1736,48857,48858,48860,48862],{"class":1738,"line":1772},[1736,48859,10426],{"class":1912},[1736,48861,10429],{"class":2674},[1736,48863,14949],{"class":1912},[1736,48865,48866],{"class":1738,"line":1778},[1736,48867,48868],{"class":1935},"  'should render a button as a Link, checks for href attribute and primary class'\n",[1736,48870,48871],{"class":1738,"line":1784},[1736,48872,7045],{"class":1912},[11,48874,48875],{},"Note: I am using 'it' instead of 'test' but I believe it is just a matter of preference so choose which you like best.",[138,48877,48879],{"id":48878},"rendering-the-button","Rendering the button",[11,48881,48882],{},"We can remove the todo from the first test and render the primary button by adding an arrow function after the test description.",[299,48884,48886],{"className":4894,"code":48885,"language":4896,"meta":307,"style":307},"it('should render a button with the class of primary', () => {\n  render(\u003CPrimaryButton />)\n})\n",[179,48887,48888,48902,48913],{"__ignoreMap":307},[1736,48889,48890,48892,48894,48896,48898,48900],{"class":1738,"line":1739},[1736,48891,10517],{"class":2674},[1736,48893,7751],{"class":1912},[1736,48895,48789],{"class":1935},[1736,48897,10524],{"class":1912},[1736,48899,7013],{"class":4866},[1736,48901,4914],{"class":1912},[1736,48903,48904,48906,48908,48911],{"class":1738,"line":748},[1736,48905,10533],{"class":2674},[1736,48907,10536],{"class":1912},[1736,48909,48910],{"class":1918},"PrimaryButton",[1736,48912,10541],{"class":1912},[1736,48914,48915],{"class":1738,"line":756},[1736,48916,10582],{"class":1912},[138,48918,48920],{"id":48919},"using-roles-to-find-our-button","Using Roles to find our button",[11,48922,48923,48924,48926,48927,48929,48930,48932,48933,48935,48936,48938],{},"We then use the ",[179,48925,10346],{}," method followed by the ",[179,48928,1032],{}," function passing in the role of ",[179,48931,13023],{},". We do this because we want to see what roles are available to us. It won't find a role of ",[179,48934,13023],{}," but it will tell us the role of ",[179,48937,14849],{}," is available. This was an obvious one but sometimes you might not know what role is available to you so doing this can really help.",[299,48940,48942],{"className":4894,"code":48941,"language":4896,"meta":307,"style":307},"it('should render a button with the class of primary', () => {\n  render(\u003CPrimaryButton />)\n  const primaryButton = screen.getByRole('blah')\n})\n",[179,48943,48944,48958,48968,48987],{"__ignoreMap":307},[1736,48945,48946,48948,48950,48952,48954,48956],{"class":1738,"line":1739},[1736,48947,10517],{"class":2674},[1736,48949,7751],{"class":1912},[1736,48951,48789],{"class":1935},[1736,48953,10524],{"class":1912},[1736,48955,7013],{"class":4866},[1736,48957,4914],{"class":1912},[1736,48959,48960,48962,48964,48966],{"class":1738,"line":748},[1736,48961,10533],{"class":2674},[1736,48963,10536],{"class":1912},[1736,48965,48910],{"class":1918},[1736,48967,10541],{"class":1912},[1736,48969,48970,48972,48975,48977,48979,48981,48983,48985],{"class":1738,"line":756},[1736,48971,7824],{"class":4866},[1736,48973,48974],{"class":1918}," primaryButton",[1736,48976,4911],{"class":4866},[1736,48978,10551],{"class":1912},[1736,48980,1032],{"class":2674},[1736,48982,7751],{"class":1912},[1736,48984,13011],{"class":1935},[1736,48986,7045],{"class":1912},[1736,48988,48989],{"class":1738,"line":1755},[1736,48990,10582],{"class":1912},[11,48992,48993,48994,3733,48996,48998,48999,49001],{},"Let's change our role of ",[179,48995,13023],{},[179,48997,14849],{},". The second argument we pass in is the text that we want to test for. By passing it in as a regex instead of a string we add the ",[179,49000,13031],{}," after the word we want to test for and then we don't have to worry about capital letters.",[299,49003,49005],{"className":4894,"code":49004,"language":4896,"meta":307,"style":307},"it('should render a button with the class of primary', () => {\n  render(\u003CPrimaryButton />)\n  const primaryButton = screen.getByRole('button', { name: /primary/i })\n})\n",[179,49006,49007,49021,49031,49060],{"__ignoreMap":307},[1736,49008,49009,49011,49013,49015,49017,49019],{"class":1738,"line":1739},[1736,49010,10517],{"class":2674},[1736,49012,7751],{"class":1912},[1736,49014,48789],{"class":1935},[1736,49016,10524],{"class":1912},[1736,49018,7013],{"class":4866},[1736,49020,4914],{"class":1912},[1736,49022,49023,49025,49027,49029],{"class":1738,"line":748},[1736,49024,10533],{"class":2674},[1736,49026,10536],{"class":1912},[1736,49028,48910],{"class":1918},[1736,49030,10541],{"class":1912},[1736,49032,49033,49035,49037,49039,49041,49043,49045,49047,49049,49051,49054,49056,49058],{"class":1738,"line":756},[1736,49034,7824],{"class":4866},[1736,49036,48974],{"class":1918},[1736,49038,4911],{"class":4866},[1736,49040,10551],{"class":1912},[1736,49042,1032],{"class":2674},[1736,49044,7751],{"class":1912},[1736,49046,10682],{"class":1935},[1736,49048,27080],{"class":1912},[1736,49050,13133],{"class":1935},[1736,49052,49053],{"class":13136},"primary",[1736,49055,1066],{"class":1935},[1736,49057,13031],{"class":4866},[1736,49059,10691],{"class":1912},[1736,49061,49062],{"class":1738,"line":1755},[1736,49063,10582],{"class":1912},[138,49065,49067],{"id":49066},"expect-our-button-to-have-a-class-of-primary","Expect our button to have a class of primary",[11,49069,49070,49071,49073,49074,49076,49077,9384],{},"We then expect our button to have a class of ",[179,49072,49053],{},". We can do this by using the ",[179,49075,13161],{}," function and passing in the button we want to test and then the class we want to test for using the ",[179,49078,49079],{},"toHaveClass",[299,49081,49083],{"className":4894,"code":49082,"language":4896,"meta":307,"style":307},"it('should render a button with the class of primary', () => {\n  render(\u003CPrimaryButton />)\n  const primaryButton = screen.getByRole('button', { name: /primary/i })\n  expect(primaryButton).toHaveClass('primary')\n})\n",[179,49084,49085,49099,49109,49137,49153],{"__ignoreMap":307},[1736,49086,49087,49089,49091,49093,49095,49097],{"class":1738,"line":1739},[1736,49088,10517],{"class":2674},[1736,49090,7751],{"class":1912},[1736,49092,48789],{"class":1935},[1736,49094,10524],{"class":1912},[1736,49096,7013],{"class":4866},[1736,49098,4914],{"class":1912},[1736,49100,49101,49103,49105,49107],{"class":1738,"line":748},[1736,49102,10533],{"class":2674},[1736,49104,10536],{"class":1912},[1736,49106,48910],{"class":1918},[1736,49108,10541],{"class":1912},[1736,49110,49111,49113,49115,49117,49119,49121,49123,49125,49127,49129,49131,49133,49135],{"class":1738,"line":756},[1736,49112,7824],{"class":4866},[1736,49114,48974],{"class":1918},[1736,49116,4911],{"class":4866},[1736,49118,10551],{"class":1912},[1736,49120,1032],{"class":2674},[1736,49122,7751],{"class":1912},[1736,49124,10682],{"class":1935},[1736,49126,27080],{"class":1912},[1736,49128,13133],{"class":1935},[1736,49130,49053],{"class":13136},[1736,49132,1066],{"class":1935},[1736,49134,13031],{"class":4866},[1736,49136,10691],{"class":1912},[1736,49138,49139,49141,49144,49146,49148,49151],{"class":1738,"line":1755},[1736,49140,10565],{"class":2674},[1736,49142,49143],{"class":1912},"(primaryButton).",[1736,49145,49079],{"class":2674},[1736,49147,7751],{"class":1912},[1736,49149,49150],{"class":1935},"'primary'",[1736,49152,7045],{"class":1912},[1736,49154,49155],{"class":1738,"line":1761},[1736,49156,10582],{"class":1912},[138,49158,49160],{"id":49159},"check-your-tests-also-fail","Check your tests also fail",[11,49162,49163],{},"We now should have a green check mark next to our test. But of course we should also make sure our test fails if we pass in the class of secondary for example.",[11,49165,49166],{},"If using Bit like I am you can see the tests direct in the UI or by running the command:",[299,49168,49170],{"className":2665,"code":49169,"language":2667,"meta":307,"style":307},"bit test componentId --watch\n",[179,49171,49172],{"__ignoreMap":307},[1736,49173,49174,49176,49178,49181],{"class":1738,"line":1739},[1736,49175,4970],{"class":2674},[1736,49177,22613],{"class":1935},[1736,49179,49180],{"class":1935}," componentId",[1736,49182,49183],{"class":1918}," --watch\n",[11,49185,49186],{},"From there we can go on and test the rest of our button compositions.",[138,49188,49190],{"id":49189},"testing-disabled-buttons","Testing Disabled Buttons",[11,49192,49193,49194,9384],{},"To test that a button is disabled we can use the ",[179,49195,49196],{},"toBeDisabled",[299,49198,49200],{"className":4894,"code":49199,"language":4896,"meta":307,"style":307},"it('should render a disabled button with the class of primary', () => {\n  render(\u003CPrimaryButtonDisabled />)\n  const primaryButtonDisabled = screen.getByRole('button', {\n    name: /primary/i\n  })\n  expect(primaryButtonDisabled).toHaveClass('primary')\n  expect(primaryButtonDisabled).toBeDisabled()\n})\n",[179,49201,49202,49216,49227,49246,49258,49262,49277,49287],{"__ignoreMap":307},[1736,49203,49204,49206,49208,49210,49212,49214],{"class":1738,"line":1739},[1736,49205,10517],{"class":2674},[1736,49207,7751],{"class":1912},[1736,49209,48802],{"class":1935},[1736,49211,10524],{"class":1912},[1736,49213,7013],{"class":4866},[1736,49215,4914],{"class":1912},[1736,49217,49218,49220,49222,49225],{"class":1738,"line":748},[1736,49219,10533],{"class":2674},[1736,49221,10536],{"class":1912},[1736,49223,49224],{"class":1918},"PrimaryButtonDisabled",[1736,49226,10541],{"class":1912},[1736,49228,49229,49231,49234,49236,49238,49240,49242,49244],{"class":1738,"line":756},[1736,49230,7824],{"class":4866},[1736,49232,49233],{"class":1918}," primaryButtonDisabled",[1736,49235,4911],{"class":4866},[1736,49237,10551],{"class":1912},[1736,49239,1032],{"class":2674},[1736,49241,7751],{"class":1912},[1736,49243,10682],{"class":1935},[1736,49245,13125],{"class":1912},[1736,49247,49248,49250,49252,49254,49256],{"class":1738,"line":1755},[1736,49249,13130],{"class":1912},[1736,49251,13133],{"class":1935},[1736,49253,49053],{"class":13136},[1736,49255,1066],{"class":1935},[1736,49257,13142],{"class":4866},[1736,49259,49260],{"class":1738,"line":1761},[1736,49261,13147],{"class":1912},[1736,49263,49264,49266,49269,49271,49273,49275],{"class":1738,"line":1767},[1736,49265,10565],{"class":2674},[1736,49267,49268],{"class":1912},"(primaryButtonDisabled).",[1736,49270,49079],{"class":2674},[1736,49272,7751],{"class":1912},[1736,49274,49150],{"class":1935},[1736,49276,7045],{"class":1912},[1736,49278,49279,49281,49283,49285],{"class":1738,"line":1772},[1736,49280,10565],{"class":2674},[1736,49282,49268],{"class":1912},[1736,49284,49196],{"class":2674},[1736,49286,22680],{"class":1912},[1736,49288,49289],{"class":1738,"line":1778},[1736,49290,10582],{"class":1912},[138,49292,49294],{"id":49293},"testing-button-as-a-link","Testing Button as a Link",[11,49296,49297,49298,49301,49302,49304,49305,49307,49308,49310,49311,49313],{},"Our button component can take the prop of ",[179,49299,49300],{},"link"," which will render the button as a Link in other words as an ",[179,49303,26856],{}," element. We can test this by checking to see if it has the role of ",[179,49306,49300],{}," as well as if it has the ",[179,49309,26863],{}," attribute as a link with no ",[179,49312,26863],{}," won't really do much.",[299,49315,49317],{"className":4894,"code":49316,"language":4896,"meta":307,"style":307},"it('should render a button as a Link, checks for href attribute and primary class', () => {\n  render(\u003CButtonAsLink />)\n  const buttonAsLink = screen.getByRole('link', { name: /link/i })\n  expect(buttonAsLink).toHaveClass('primary')\n  expect(buttonAsLink).toHaveAttribute('href', '/')\n})\n",[179,49318,49319,49334,49345,49374,49389,49409],{"__ignoreMap":307},[1736,49320,49321,49323,49325,49328,49330,49332],{"class":1738,"line":1739},[1736,49322,10517],{"class":2674},[1736,49324,7751],{"class":1912},[1736,49326,49327],{"class":1935},"'should render a button as a Link, checks for href attribute and primary class'",[1736,49329,10524],{"class":1912},[1736,49331,7013],{"class":4866},[1736,49333,4914],{"class":1912},[1736,49335,49336,49338,49340,49343],{"class":1738,"line":748},[1736,49337,10533],{"class":2674},[1736,49339,10536],{"class":1912},[1736,49341,49342],{"class":1918},"ButtonAsLink",[1736,49344,10541],{"class":1912},[1736,49346,49347,49349,49352,49354,49356,49358,49360,49362,49364,49366,49368,49370,49372],{"class":1738,"line":756},[1736,49348,7824],{"class":4866},[1736,49350,49351],{"class":1918}," buttonAsLink",[1736,49353,4911],{"class":4866},[1736,49355,10551],{"class":1912},[1736,49357,1032],{"class":2674},[1736,49359,7751],{"class":1912},[1736,49361,27009],{"class":1935},[1736,49363,27080],{"class":1912},[1736,49365,13133],{"class":1935},[1736,49367,49300],{"class":13136},[1736,49369,1066],{"class":1935},[1736,49371,13031],{"class":4866},[1736,49373,10691],{"class":1912},[1736,49375,49376,49378,49381,49383,49385,49387],{"class":1738,"line":1755},[1736,49377,10565],{"class":2674},[1736,49379,49380],{"class":1912},"(buttonAsLink).",[1736,49382,49079],{"class":2674},[1736,49384,7751],{"class":1912},[1736,49386,49150],{"class":1935},[1736,49388,7045],{"class":1912},[1736,49390,49391,49393,49395,49398,49400,49403,49405,49407],{"class":1738,"line":1761},[1736,49392,10565],{"class":2674},[1736,49394,49380],{"class":1912},[1736,49396,49397],{"class":2674},"toHaveAttribute",[1736,49399,7751],{"class":1912},[1736,49401,49402],{"class":1935},"'href'",[1736,49404,829],{"class":1912},[1736,49406,21291],{"class":1935},[1736,49408,7045],{"class":1912},[1736,49410,49411],{"class":1738,"line":1767},[1736,49412,10582],{"class":1912},[11,49414,49415],{},"And there we have it. All our tests are passing and we can safely use our component in other components knowing that it will work the way it should and if anyone changes anything the tests will fail. Although testing takes time it can save you so much time later on. The more we write tests the more comfortable we feel and the better we get at it and if we can make it part of our building process then things become so much easier. But at first take it step by step and just start small like I did.",[11,49417,22497],{},[70,49419,49420,49427,49433,49440,49447,49454],{},[73,49421,49422],{},[15,49423,49426],{"href":49424,"rel":49425},"https://bit.dev/learn-bit-react/base-ui/ui/button/~code/button.spec.tsx",[19],"Button Component Test files",[73,49428,49429],{},[15,49430,49432],{"href":5197,"rel":49431},[19],"Button Component overview",[73,49434,49435],{},[15,49436,49439],{"href":49437,"rel":49438},"https://bit.dev/learn-bit-react/base-ui/ui/button/~code/button.tsx",[19],"Button Component code",[73,49441,49442],{},[15,49443,49446],{"href":49444,"rel":49445},"https://bit.dev/learn-bit-react/base-ui/ui/button/~code/button.composition.tsx",[19],"Button Component Compositions code",[73,49448,49449],{},[15,49450,49453],{"href":49451,"rel":49452},"https://testing-library.com/docs/",[19],"Testing Library docs",[73,49455,49456],{},[15,49457,49460],{"href":49458,"rel":49459},"https://kentcdodds.com/blog/common-mistakes-with-react-testing-library",[19],"Common mistakes with React Testing Library by Kent C. Dodds",[2011,49462,49463],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":307,"searchDepth":748,"depth":748,"links":49465},[49466,49467],{"id":48633,"depth":748,"text":48634},{"id":48664,"depth":748,"text":48665,"children":49468},[49469,49470,49471,49472,49473,49474,49475,49476],{"id":48683,"depth":756,"text":48684},{"id":48769,"depth":756,"text":48770},{"id":48878,"depth":756,"text":48879},{"id":48919,"depth":756,"text":48920},{"id":49066,"depth":756,"text":49067},{"id":49159,"depth":756,"text":49160},{"id":49189,"depth":756,"text":49190},{"id":49293,"depth":756,"text":49294},"2022-01-07","Testing a button component with React Testing Library. What to test and how to test your button component when building multiple compositions of the button.","v1612443662/debbie.codes/blog/button-testing_2x_oliu20",{},"/blog/testing-button-component",{"title":48617,"description":49478},"blog/testing-button-component",[5221,1411],"1Jb_7sOdJSnPlODNglkVNJ8IZrcy2mQIkj2FzJGxvqg",{"id":49487,"title":49488,"body":49489,"canonical":788,"date":50864,"description":50865,"extension":786,"featured":787,"image":50866,"meta":50867,"navigation":790,"ogimage":788,"path":50869,"provider":3460,"published":790,"seo":50870,"stem":50871,"tags":50872,"url":788,"__hash__":50873},"blog/blog/testing-color-mode.md","Testing a Sites Color Mode with Playwright",{"type":8,"value":49490,"toc":50853},[49491,49494,49497,49499,49513,49523,49582,49586,49603,49652,49656,49661,49763,49767,49776,49869,49873,49876,49996,50000,50003,50203,50207,50214,50341,50345,50348,50836,50840,50843,50850],[11,49492,49493],{},"My website uses the Nuxt color mode module to allow the user to change the theme of the site. The user can choose between light, dark, sepia or use the system preference. Take a look at the color mode at the bottom of this page to see it in action.",[11,49495,49496],{},"I wanted to write a test to make sure that the color mode is working as expected especially after it stopped working due to an update and I only found out after someone sent me a DM on Twitter to tell me. Funnily enough I had actually written a test for this but it was not working as expected. I wasn't testing everything I should so although the user was changing the theme to light mode, the message from the color picker was saying light mode but the html class was not updating and therefore the theme was not changing. It was time to write better tests.",[23,49498,15435],{"id":15434},[11,49500,49501,49502,49507,49508,608,49510,49512],{},"First ",[15,49503,49506],{"href":49504,"rel":49505},"https://playwright.dev/docs/intro",[19],"install Playwright"," and then import ",[179,49509,25293],{},[179,49511,13161],{}," from Playwright's test runner. Playwright uses the expect library from Jest so you can use the same matchers as you would in Jest.",[11,49514,49515,49516,49518,49519,49522],{},"Before each test we want to go to the home page and test from there as the color mode module is not available on all pages. We can do this by adding a ",[179,49517,10597],{}," function and then calling ",[179,49520,49521],{},"page.goto"," to go to the home page.",[299,49524,49526],{"className":8734,"code":49525,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test'\n\ntest.beforeEach(async ({ page }) => {\n  await page.goto('/')\n})\n",[179,49527,49528,49540,49544,49564,49578],{"__ignoreMap":307},[1736,49529,49530,49532,49535,49537],{"class":1738,"line":1739},[1736,49531,4996],{"class":4866},[1736,49533,49534],{"class":1912}," { expect, test } ",[1736,49536,5002],{"class":4866},[1736,49538,49539],{"class":1935}," '@playwright/test'\n",[1736,49541,49542],{"class":1738,"line":748},[1736,49543,1747],{"emptyLinePlaceholder":790},[1736,49545,49546,49548,49550,49552,49554,49556,49558,49560,49562],{"class":1738,"line":756},[1736,49547,32280],{"class":1912},[1736,49549,10597],{"class":2674},[1736,49551,7751],{"class":1912},[1736,49553,15790],{"class":4866},[1736,49555,17735],{"class":1912},[1736,49557,25307],{"class":5036},[1736,49559,7778],{"class":1912},[1736,49561,7013],{"class":4866},[1736,49563,4914],{"class":1912},[1736,49565,49566,49568,49570,49572,49574,49576],{"class":1738,"line":1755},[1736,49567,25323],{"class":4866},[1736,49569,22665],{"class":1912},[1736,49571,25328],{"class":2674},[1736,49573,7751],{"class":1912},[1736,49575,21291],{"class":1935},[1736,49577,7045],{"class":1912},[1736,49579,49580],{"class":1738,"line":1761},[1736,49581,10582],{"class":1912},[23,49583,49585],{"id":49584},"setting-the-base","Setting the Base",[11,49587,49588,49589,49592,49593,49596,49597,49599,49600,891],{},"I have set the Base URL in the ",[179,49590,49591],{},"playwright.config"," file as the ",[179,49594,49595],{},"localhost: 8888",". The port is 8888 as that is the port I have set in my ",[179,49598,17364],{}," file in the dev script using ",[179,49601,49602],{},"--port 8888",[299,49604,49606],{"className":8734,"code":49605,"language":8736,"meta":307,"style":307},"const config: PlaywrightTestConfig = {\n  use: {\n    /* Base URL to use in actions like `await page.goto('/')`. */\n    baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:8888'\n  }\n}\n",[179,49607,49608,49622,49626,49631,49644,49648],{"__ignoreMap":307},[1736,49609,49610,49612,49614,49616,49618,49620],{"class":1738,"line":1739},[1736,49611,5029],{"class":4866},[1736,49613,48406],{"class":1918},[1736,49615,1087],{"class":4866},[1736,49617,48411],{"class":2674},[1736,49619,4911],{"class":4866},[1736,49621,4914],{"class":1912},[1736,49623,49624],{"class":1738,"line":748},[1736,49625,48424],{"class":1912},[1736,49627,49628],{"class":1738,"line":756},[1736,49629,49630],{"class":6820},"    /* Base URL to use in actions like `await page.goto('/')`. */\n",[1736,49632,49633,49636,49639,49641],{"class":1738,"line":1755},[1736,49634,49635],{"class":1912},"    baseURL: process.env.",[1736,49637,49638],{"class":1918},"PLAYWRIGHT_TEST_BASE_URL",[1736,49640,43417],{"class":4866},[1736,49642,49643],{"class":1935}," 'http://localhost:8888'\n",[1736,49645,49646],{"class":1738,"line":1761},[1736,49647,1971],{"class":1912},[1736,49649,49650],{"class":1738,"line":1767},[1736,49651,1976],{"class":1912},[23,49653,49655],{"id":49654},"running-the-nuxt-server","Running the Nuxt server",[11,49657,49658,49659,2900],{},"I am running my tests on a local build rather than a staging or preview environment so in order to set this I add the following to my ",[179,49660,49591],{},[299,49662,49664],{"className":8734,"code":49663,"language":8736,"meta":307,"style":307},"const config: PlaywrightTestConfig = {\n  /* Run your local dev server before starting the tests */\n  webServer: process.env.CI\n    ? {\n        command: 'yarn dev',\n        port: 8888,\n        timeout: 120 * 1000,\n        reuseExistingServer: !process.env.CI\n      }\n    : undefined\n}\n",[179,49665,49666,49680,49685,49693,49700,49710,49720,49735,49747,49751,49759],{"__ignoreMap":307},[1736,49667,49668,49670,49672,49674,49676,49678],{"class":1738,"line":1739},[1736,49669,5029],{"class":4866},[1736,49671,48406],{"class":1918},[1736,49673,1087],{"class":4866},[1736,49675,48411],{"class":2674},[1736,49677,4911],{"class":4866},[1736,49679,4914],{"class":1912},[1736,49681,49682],{"class":1738,"line":748},[1736,49683,49684],{"class":6820},"  /* Run your local dev server before starting the tests */\n",[1736,49686,49687,49690],{"class":1738,"line":756},[1736,49688,49689],{"class":1912},"  webServer: process.env.",[1736,49691,49692],{"class":1918},"CI\n",[1736,49694,49695,49698],{"class":1738,"line":1755},[1736,49696,49697],{"class":4866},"    ?",[1736,49699,4914],{"class":1912},[1736,49701,49702,49705,49708],{"class":1738,"line":1761},[1736,49703,49704],{"class":1912},"        command: ",[1736,49706,49707],{"class":1935},"'yarn dev'",[1736,49709,1939],{"class":1912},[1736,49711,49712,49715,49718],{"class":1738,"line":1767},[1736,49713,49714],{"class":1912},"        port: ",[1736,49716,49717],{"class":1918},"8888",[1736,49719,1939],{"class":1912},[1736,49721,49722,49725,49728,49730,49733],{"class":1738,"line":1772},[1736,49723,49724],{"class":1912},"        timeout: ",[1736,49726,49727],{"class":1918},"120",[1736,49729,46278],{"class":4866},[1736,49731,49732],{"class":1918}," 1000",[1736,49734,1939],{"class":1912},[1736,49736,49737,49740,49742,49745],{"class":1738,"line":1778},[1736,49738,49739],{"class":1912},"        reuseExistingServer: ",[1736,49741,1690],{"class":4866},[1736,49743,49744],{"class":1912},"process.env.",[1736,49746,49692],{"class":1918},[1736,49748,49749],{"class":1738,"line":1784},[1736,49750,14448],{"class":1912},[1736,49752,49753,49756],{"class":1738,"line":1790},[1736,49754,49755],{"class":4866},"    :",[1736,49757,49758],{"class":1918}," undefined\n",[1736,49760,49761],{"class":1738,"line":1796},[1736,49762,1976],{"class":1912},[23,49764,49766],{"id":49765},"testing-light-mode","Testing light mode",[11,49768,49769,49770,49772,49773,49775],{},"Next we can test the color mode picker starting with a ",[179,49771,17717],{}," block where we will add all our tests. Our first test will test the page in light mode. We use locators to find an element on a page and selectors to select the ",[179,49774,27145],{}," selector as this also tests our accessability. We then expect the locator with the text of color mode to have the text of light and expect it to be visible on the page.",[299,49777,49779],{"className":8734,"code":49778,"language":8736,"meta":307,"style":307},"test.describe('Color Picker', () => {\n  test('shows page in light mode', async ({ page }) => {\n    await page.locator('[aria-label=\"light mode\"]').click()\n    await expect(page.locator('text=color mode >> text=light')).toBeVisible()\n  })\n})\n",[179,49780,49781,49798,49821,49840,49861,49865],{"__ignoreMap":307},[1736,49782,49783,49785,49787,49789,49792,49794,49796],{"class":1738,"line":1739},[1736,49784,32280],{"class":1912},[1736,49786,17717],{"class":2674},[1736,49788,7751],{"class":1912},[1736,49790,49791],{"class":1935},"'Color Picker'",[1736,49793,10524],{"class":1912},[1736,49795,7013],{"class":4866},[1736,49797,4914],{"class":1912},[1736,49799,49800,49802,49804,49807,49809,49811,49813,49815,49817,49819],{"class":1738,"line":748},[1736,49801,32298],{"class":2674},[1736,49803,7751],{"class":1912},[1736,49805,49806],{"class":1935},"'shows page in light mode'",[1736,49808,829],{"class":1912},[1736,49810,15790],{"class":4866},[1736,49812,17735],{"class":1912},[1736,49814,25307],{"class":5036},[1736,49816,7778],{"class":1912},[1736,49818,7013],{"class":4866},[1736,49820,4914],{"class":1912},[1736,49822,49823,49825,49827,49829,49831,49834,49836,49838],{"class":1738,"line":756},[1736,49824,16153],{"class":4866},[1736,49826,22665],{"class":1912},[1736,49828,22668],{"class":2674},[1736,49830,7751],{"class":1912},[1736,49832,49833],{"class":1935},"'[aria-label=\"light mode\"]'",[1736,49835,911],{"class":1912},[1736,49837,10804],{"class":2674},[1736,49839,22680],{"class":1912},[1736,49841,49842,49844,49846,49848,49850,49852,49855,49857,49859],{"class":1738,"line":1755},[1736,49843,16153],{"class":4866},[1736,49845,25447],{"class":2674},[1736,49847,25450],{"class":1912},[1736,49849,22668],{"class":2674},[1736,49851,7751],{"class":1912},[1736,49853,49854],{"class":1935},"'text=color mode >> text=light'",[1736,49856,32734],{"class":1912},[1736,49858,25473],{"class":2674},[1736,49860,22680],{"class":1912},[1736,49862,49863],{"class":1738,"line":1761},[1736,49864,13147],{"class":1912},[1736,49866,49867],{"class":1738,"line":1767},[1736,49868,10582],{"class":1912},[23,49870,49872],{"id":49871},"testing-the-html-class","Testing the HTML class",[11,49874,49875],{},"This test will work but really is only testing that the text updated on the page. What if the css class never updated which is what happened on my site. The module adds a css class of 'light' or 'dark' to the html element but you may have something in your config file that stops this from updating or the module might have changed. So how can we make sure the css class is also giving us the right mode? We can do this by using a selector to select the html element and check it contains an attribute with the class of 'light'.",[299,49877,49879],{"className":8734,"code":49878,"language":8736,"meta":307,"style":307},"test.describe('Color Picker', () => {\n  test('shows page in light mode', async ({ page }) => {\n    await page.locator('[aria-label=\"light mode\"]').click()\n    await expect(page.locator('text=color mode >> text=light')).toBeVisible()\n    await expect(page.locator('html')).toHaveAttribute('class', 'light')\n  })\n})\n",[179,49880,49881,49897,49919,49937,49957,49988,49992],{"__ignoreMap":307},[1736,49882,49883,49885,49887,49889,49891,49893,49895],{"class":1738,"line":1739},[1736,49884,32280],{"class":1912},[1736,49886,17717],{"class":2674},[1736,49888,7751],{"class":1912},[1736,49890,49791],{"class":1935},[1736,49892,10524],{"class":1912},[1736,49894,7013],{"class":4866},[1736,49896,4914],{"class":1912},[1736,49898,49899,49901,49903,49905,49907,49909,49911,49913,49915,49917],{"class":1738,"line":748},[1736,49900,32298],{"class":2674},[1736,49902,7751],{"class":1912},[1736,49904,49806],{"class":1935},[1736,49906,829],{"class":1912},[1736,49908,15790],{"class":4866},[1736,49910,17735],{"class":1912},[1736,49912,25307],{"class":5036},[1736,49914,7778],{"class":1912},[1736,49916,7013],{"class":4866},[1736,49918,4914],{"class":1912},[1736,49920,49921,49923,49925,49927,49929,49931,49933,49935],{"class":1738,"line":756},[1736,49922,16153],{"class":4866},[1736,49924,22665],{"class":1912},[1736,49926,22668],{"class":2674},[1736,49928,7751],{"class":1912},[1736,49930,49833],{"class":1935},[1736,49932,911],{"class":1912},[1736,49934,10804],{"class":2674},[1736,49936,22680],{"class":1912},[1736,49938,49939,49941,49943,49945,49947,49949,49951,49953,49955],{"class":1738,"line":1755},[1736,49940,16153],{"class":4866},[1736,49942,25447],{"class":2674},[1736,49944,25450],{"class":1912},[1736,49946,22668],{"class":2674},[1736,49948,7751],{"class":1912},[1736,49950,49854],{"class":1935},[1736,49952,32734],{"class":1912},[1736,49954,25473],{"class":2674},[1736,49956,22680],{"class":1912},[1736,49958,49959,49961,49963,49965,49967,49969,49972,49974,49976,49978,49981,49983,49986],{"class":1738,"line":1761},[1736,49960,16153],{"class":4866},[1736,49962,25447],{"class":2674},[1736,49964,25450],{"class":1912},[1736,49966,22668],{"class":2674},[1736,49968,7751],{"class":1912},[1736,49970,49971],{"class":1935},"'html'",[1736,49973,32734],{"class":1912},[1736,49975,49397],{"class":2674},[1736,49977,7751],{"class":1912},[1736,49979,49980],{"class":1935},"'class'",[1736,49982,829],{"class":1912},[1736,49984,49985],{"class":1935},"'light'",[1736,49987,7045],{"class":1912},[1736,49989,49990],{"class":1738,"line":1767},[1736,49991,13147],{"class":1912},[1736,49993,49994],{"class":1738,"line":1772},[1736,49995,10582],{"class":1912},[23,49997,49999],{"id":49998},"testing-dark-mode-and-sepia","Testing dark mode and sepia",[11,50001,50002],{},"Now we can do the same for dark mode and sepia mode.",[299,50004,50006],{"className":8734,"code":50005,"language":8736,"meta":307,"style":307},"test('shows page in dark mode', async ({ page }) => {\n  await page.locator('[aria-label=\"dark mode\"]').click()\n  await expect(page.locator('text=color mode >> text=dark')).toBeVisible()\n  await expect(page.locator('html')).toHaveAttribute('class', 'dark')\n})\n\ntest('shows page in sepia mode', async ({ page }) => {\n  await page.locator('[aria-label=\"sepia mode\"]').click()\n  await expect(page.locator('text=color mode >> text=sepia')).toBeVisible()\n  await expect(page.locator('html')).toHaveAttribute('class', 'sepia')\n})\n\n",[179,50007,50008,50031,50050,50071,50099,50103,50107,50130,50149,50170,50199],{"__ignoreMap":307},[1736,50009,50010,50012,50014,50017,50019,50021,50023,50025,50027,50029],{"class":1738,"line":1739},[1736,50011,25293],{"class":2674},[1736,50013,7751],{"class":1912},[1736,50015,50016],{"class":1935},"'shows page in dark mode'",[1736,50018,829],{"class":1912},[1736,50020,15790],{"class":4866},[1736,50022,17735],{"class":1912},[1736,50024,25307],{"class":5036},[1736,50026,7778],{"class":1912},[1736,50028,7013],{"class":4866},[1736,50030,4914],{"class":1912},[1736,50032,50033,50035,50037,50039,50041,50044,50046,50048],{"class":1738,"line":748},[1736,50034,25323],{"class":4866},[1736,50036,22665],{"class":1912},[1736,50038,22668],{"class":2674},[1736,50040,7751],{"class":1912},[1736,50042,50043],{"class":1935},"'[aria-label=\"dark mode\"]'",[1736,50045,911],{"class":1912},[1736,50047,10804],{"class":2674},[1736,50049,22680],{"class":1912},[1736,50051,50052,50054,50056,50058,50060,50062,50065,50067,50069],{"class":1738,"line":756},[1736,50053,25323],{"class":4866},[1736,50055,25447],{"class":2674},[1736,50057,25450],{"class":1912},[1736,50059,22668],{"class":2674},[1736,50061,7751],{"class":1912},[1736,50063,50064],{"class":1935},"'text=color mode >> text=dark'",[1736,50066,32734],{"class":1912},[1736,50068,25473],{"class":2674},[1736,50070,22680],{"class":1912},[1736,50072,50073,50075,50077,50079,50081,50083,50085,50087,50089,50091,50093,50095,50097],{"class":1738,"line":1755},[1736,50074,25323],{"class":4866},[1736,50076,25447],{"class":2674},[1736,50078,25450],{"class":1912},[1736,50080,22668],{"class":2674},[1736,50082,7751],{"class":1912},[1736,50084,49971],{"class":1935},[1736,50086,32734],{"class":1912},[1736,50088,49397],{"class":2674},[1736,50090,7751],{"class":1912},[1736,50092,49980],{"class":1935},[1736,50094,829],{"class":1912},[1736,50096,34907],{"class":1935},[1736,50098,7045],{"class":1912},[1736,50100,50101],{"class":1738,"line":1761},[1736,50102,10582],{"class":1912},[1736,50104,50105],{"class":1738,"line":1767},[1736,50106,1747],{"emptyLinePlaceholder":790},[1736,50108,50109,50111,50113,50116,50118,50120,50122,50124,50126,50128],{"class":1738,"line":1772},[1736,50110,25293],{"class":2674},[1736,50112,7751],{"class":1912},[1736,50114,50115],{"class":1935},"'shows page in sepia mode'",[1736,50117,829],{"class":1912},[1736,50119,15790],{"class":4866},[1736,50121,17735],{"class":1912},[1736,50123,25307],{"class":5036},[1736,50125,7778],{"class":1912},[1736,50127,7013],{"class":4866},[1736,50129,4914],{"class":1912},[1736,50131,50132,50134,50136,50138,50140,50143,50145,50147],{"class":1738,"line":1778},[1736,50133,25323],{"class":4866},[1736,50135,22665],{"class":1912},[1736,50137,22668],{"class":2674},[1736,50139,7751],{"class":1912},[1736,50141,50142],{"class":1935},"'[aria-label=\"sepia mode\"]'",[1736,50144,911],{"class":1912},[1736,50146,10804],{"class":2674},[1736,50148,22680],{"class":1912},[1736,50150,50151,50153,50155,50157,50159,50161,50164,50166,50168],{"class":1738,"line":1784},[1736,50152,25323],{"class":4866},[1736,50154,25447],{"class":2674},[1736,50156,25450],{"class":1912},[1736,50158,22668],{"class":2674},[1736,50160,7751],{"class":1912},[1736,50162,50163],{"class":1935},"'text=color mode >> text=sepia'",[1736,50165,32734],{"class":1912},[1736,50167,25473],{"class":2674},[1736,50169,22680],{"class":1912},[1736,50171,50172,50174,50176,50178,50180,50182,50184,50186,50188,50190,50192,50194,50197],{"class":1738,"line":1790},[1736,50173,25323],{"class":4866},[1736,50175,25447],{"class":2674},[1736,50177,25450],{"class":1912},[1736,50179,22668],{"class":2674},[1736,50181,7751],{"class":1912},[1736,50183,49971],{"class":1935},[1736,50185,32734],{"class":1912},[1736,50187,49397],{"class":2674},[1736,50189,7751],{"class":1912},[1736,50191,49980],{"class":1935},[1736,50193,829],{"class":1912},[1736,50195,50196],{"class":1935},"'sepia'",[1736,50198,7045],{"class":1912},[1736,50200,50201],{"class":1738,"line":1796},[1736,50202,10582],{"class":1912},[23,50204,50206],{"id":50205},"testing-system-preference","Testing system preference",[11,50208,50209,50210,50213],{},"And finally we need to test our system preference. If the user has their system set to dark mode then the page should change to dark mode when the system preference icon is selected or light mode if the system preference is light mode. So how can we test this? We can emulate the color scheme by telling your test to use the ",[179,50211,50212],{},"colorScheme"," property and set it to the value you require which in our case is dark. This will now emulate the users color preference to dark mode and we can now test that the page is in dark mode when clicked.",[299,50215,50217],{"className":8734,"code":50216,"language":8736,"meta":307,"style":307},"test.use({ colorScheme: 'dark' })\n\ntest('shows page in system mode', async ({ page }) => {\n  await page.locator('[aria-label=\"system mode\"]').click()\n  await expect(\n    page.locator('text=color mode >> text=system (dark mode detected)')\n  ).toBeVisible()\n  await expect(page.locator('html')).toHaveAttribute('class', 'dark')\n})\n\n",[179,50218,50219,50232,50236,50259,50278,50286,50300,50309,50337],{"__ignoreMap":307},[1736,50220,50221,50223,50225,50228,50230],{"class":1738,"line":1739},[1736,50222,32280],{"class":1912},[1736,50224,37119],{"class":2674},[1736,50226,50227],{"class":1912},"({ colorScheme: ",[1736,50229,34907],{"class":1935},[1736,50231,10691],{"class":1912},[1736,50233,50234],{"class":1738,"line":748},[1736,50235,1747],{"emptyLinePlaceholder":790},[1736,50237,50238,50240,50242,50245,50247,50249,50251,50253,50255,50257],{"class":1738,"line":756},[1736,50239,25293],{"class":2674},[1736,50241,7751],{"class":1912},[1736,50243,50244],{"class":1935},"'shows page in system mode'",[1736,50246,829],{"class":1912},[1736,50248,15790],{"class":4866},[1736,50250,17735],{"class":1912},[1736,50252,25307],{"class":5036},[1736,50254,7778],{"class":1912},[1736,50256,7013],{"class":4866},[1736,50258,4914],{"class":1912},[1736,50260,50261,50263,50265,50267,50269,50272,50274,50276],{"class":1738,"line":1755},[1736,50262,25323],{"class":4866},[1736,50264,22665],{"class":1912},[1736,50266,22668],{"class":2674},[1736,50268,7751],{"class":1912},[1736,50270,50271],{"class":1935},"'[aria-label=\"system mode\"]'",[1736,50273,911],{"class":1912},[1736,50275,10804],{"class":2674},[1736,50277,22680],{"class":1912},[1736,50279,50280,50282,50284],{"class":1738,"line":1761},[1736,50281,25323],{"class":4866},[1736,50283,25447],{"class":2674},[1736,50285,14949],{"class":1912},[1736,50287,50288,50291,50293,50295,50298],{"class":1738,"line":1767},[1736,50289,50290],{"class":1912},"    page.",[1736,50292,22668],{"class":2674},[1736,50294,7751],{"class":1912},[1736,50296,50297],{"class":1935},"'text=color mode >> text=system (dark mode detected)'",[1736,50299,7045],{"class":1912},[1736,50301,50302,50305,50307],{"class":1738,"line":1772},[1736,50303,50304],{"class":1912},"  ).",[1736,50306,25473],{"class":2674},[1736,50308,22680],{"class":1912},[1736,50310,50311,50313,50315,50317,50319,50321,50323,50325,50327,50329,50331,50333,50335],{"class":1738,"line":1778},[1736,50312,25323],{"class":4866},[1736,50314,25447],{"class":2674},[1736,50316,25450],{"class":1912},[1736,50318,22668],{"class":2674},[1736,50320,7751],{"class":1912},[1736,50322,49971],{"class":1935},[1736,50324,32734],{"class":1912},[1736,50326,49397],{"class":2674},[1736,50328,7751],{"class":1912},[1736,50330,49980],{"class":1935},[1736,50332,829],{"class":1912},[1736,50334,34907],{"class":1935},[1736,50336,7045],{"class":1912},[1736,50338,50339],{"class":1738,"line":1784},[1736,50340,10582],{"class":1912},[23,50342,50344],{"id":50343},"final-test","Final Test",[11,50346,50347],{},"Our final test now looks like this.",[299,50349,50351],{"className":8734,"code":50350,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test'\n\ntest.beforeEach(async ({ page }) => {\n  await page.goto('/')\n})\n\ntest.describe('Color Picker', () => {\n  test('shows page in light mode', async ({ page }) => {\n    await page.locator('[aria-label=\"light mode\"]').click()\n    await expect(page.locator('text=color mode >> text=light')).toBeVisible()\n    await expect(page.locator('html')).toHaveAttribute('class', 'light')\n  })\n\n  test('shows page in dark mode', async ({ page }) => {\n    await page.locator('[aria-label=\"dark mode\"]').click()\n    await expect(page.locator('text=color mode >> text=dark')).toBeVisible()\n    await expect(page.locator('html')).toHaveAttribute('class', 'dark')\n  })\n\n  test('shows page in sepia mode', async ({ page }) => {\n    await page.locator('[aria-label=\"sepia mode\"]').click()\n    await expect(page.locator('text=color mode >> text=sepia')).toBeVisible()\n    await expect(page.locator('html')).toHaveAttribute('class', 'sepia')\n  })\n\n  test.use({ colorScheme: 'dark' })\n\n  test('shows page in system mode', async ({ page }) => {\n    await page.locator('[aria-label=\"system mode\"]').click()\n    await expect(\n      page.locator('text=color mode >> text=system (dark mode detected)')\n    ).toBeVisible()\n    await expect(page.locator('html')).toHaveAttribute('class', 'dark')\n  })\n})\n",[179,50352,50353,50363,50367,50387,50401,50405,50409,50425,50447,50465,50485,50513,50517,50521,50543,50561,50581,50609,50613,50617,50639,50657,50677,50705,50709,50713,50726,50730,50752,50770,50778,50791,50800,50828,50832],{"__ignoreMap":307},[1736,50354,50355,50357,50359,50361],{"class":1738,"line":1739},[1736,50356,4996],{"class":4866},[1736,50358,49534],{"class":1912},[1736,50360,5002],{"class":4866},[1736,50362,49539],{"class":1935},[1736,50364,50365],{"class":1738,"line":748},[1736,50366,1747],{"emptyLinePlaceholder":790},[1736,50368,50369,50371,50373,50375,50377,50379,50381,50383,50385],{"class":1738,"line":756},[1736,50370,32280],{"class":1912},[1736,50372,10597],{"class":2674},[1736,50374,7751],{"class":1912},[1736,50376,15790],{"class":4866},[1736,50378,17735],{"class":1912},[1736,50380,25307],{"class":5036},[1736,50382,7778],{"class":1912},[1736,50384,7013],{"class":4866},[1736,50386,4914],{"class":1912},[1736,50388,50389,50391,50393,50395,50397,50399],{"class":1738,"line":1755},[1736,50390,25323],{"class":4866},[1736,50392,22665],{"class":1912},[1736,50394,25328],{"class":2674},[1736,50396,7751],{"class":1912},[1736,50398,21291],{"class":1935},[1736,50400,7045],{"class":1912},[1736,50402,50403],{"class":1738,"line":1761},[1736,50404,10582],{"class":1912},[1736,50406,50407],{"class":1738,"line":1767},[1736,50408,1747],{"emptyLinePlaceholder":790},[1736,50410,50411,50413,50415,50417,50419,50421,50423],{"class":1738,"line":1772},[1736,50412,32280],{"class":1912},[1736,50414,17717],{"class":2674},[1736,50416,7751],{"class":1912},[1736,50418,49791],{"class":1935},[1736,50420,10524],{"class":1912},[1736,50422,7013],{"class":4866},[1736,50424,4914],{"class":1912},[1736,50426,50427,50429,50431,50433,50435,50437,50439,50441,50443,50445],{"class":1738,"line":1778},[1736,50428,32298],{"class":2674},[1736,50430,7751],{"class":1912},[1736,50432,49806],{"class":1935},[1736,50434,829],{"class":1912},[1736,50436,15790],{"class":4866},[1736,50438,17735],{"class":1912},[1736,50440,25307],{"class":5036},[1736,50442,7778],{"class":1912},[1736,50444,7013],{"class":4866},[1736,50446,4914],{"class":1912},[1736,50448,50449,50451,50453,50455,50457,50459,50461,50463],{"class":1738,"line":1784},[1736,50450,16153],{"class":4866},[1736,50452,22665],{"class":1912},[1736,50454,22668],{"class":2674},[1736,50456,7751],{"class":1912},[1736,50458,49833],{"class":1935},[1736,50460,911],{"class":1912},[1736,50462,10804],{"class":2674},[1736,50464,22680],{"class":1912},[1736,50466,50467,50469,50471,50473,50475,50477,50479,50481,50483],{"class":1738,"line":1790},[1736,50468,16153],{"class":4866},[1736,50470,25447],{"class":2674},[1736,50472,25450],{"class":1912},[1736,50474,22668],{"class":2674},[1736,50476,7751],{"class":1912},[1736,50478,49854],{"class":1935},[1736,50480,32734],{"class":1912},[1736,50482,25473],{"class":2674},[1736,50484,22680],{"class":1912},[1736,50486,50487,50489,50491,50493,50495,50497,50499,50501,50503,50505,50507,50509,50511],{"class":1738,"line":1796},[1736,50488,16153],{"class":4866},[1736,50490,25447],{"class":2674},[1736,50492,25450],{"class":1912},[1736,50494,22668],{"class":2674},[1736,50496,7751],{"class":1912},[1736,50498,49971],{"class":1935},[1736,50500,32734],{"class":1912},[1736,50502,49397],{"class":2674},[1736,50504,7751],{"class":1912},[1736,50506,49980],{"class":1935},[1736,50508,829],{"class":1912},[1736,50510,49985],{"class":1935},[1736,50512,7045],{"class":1912},[1736,50514,50515],{"class":1738,"line":2353},[1736,50516,13147],{"class":1912},[1736,50518,50519],{"class":1738,"line":2358},[1736,50520,1747],{"emptyLinePlaceholder":790},[1736,50522,50523,50525,50527,50529,50531,50533,50535,50537,50539,50541],{"class":1738,"line":2364},[1736,50524,32298],{"class":2674},[1736,50526,7751],{"class":1912},[1736,50528,50016],{"class":1935},[1736,50530,829],{"class":1912},[1736,50532,15790],{"class":4866},[1736,50534,17735],{"class":1912},[1736,50536,25307],{"class":5036},[1736,50538,7778],{"class":1912},[1736,50540,7013],{"class":4866},[1736,50542,4914],{"class":1912},[1736,50544,50545,50547,50549,50551,50553,50555,50557,50559],{"class":1738,"line":2370},[1736,50546,16153],{"class":4866},[1736,50548,22665],{"class":1912},[1736,50550,22668],{"class":2674},[1736,50552,7751],{"class":1912},[1736,50554,50043],{"class":1935},[1736,50556,911],{"class":1912},[1736,50558,10804],{"class":2674},[1736,50560,22680],{"class":1912},[1736,50562,50563,50565,50567,50569,50571,50573,50575,50577,50579],{"class":1738,"line":2376},[1736,50564,16153],{"class":4866},[1736,50566,25447],{"class":2674},[1736,50568,25450],{"class":1912},[1736,50570,22668],{"class":2674},[1736,50572,7751],{"class":1912},[1736,50574,50064],{"class":1935},[1736,50576,32734],{"class":1912},[1736,50578,25473],{"class":2674},[1736,50580,22680],{"class":1912},[1736,50582,50583,50585,50587,50589,50591,50593,50595,50597,50599,50601,50603,50605,50607],{"class":1738,"line":2381},[1736,50584,16153],{"class":4866},[1736,50586,25447],{"class":2674},[1736,50588,25450],{"class":1912},[1736,50590,22668],{"class":2674},[1736,50592,7751],{"class":1912},[1736,50594,49971],{"class":1935},[1736,50596,32734],{"class":1912},[1736,50598,49397],{"class":2674},[1736,50600,7751],{"class":1912},[1736,50602,49980],{"class":1935},[1736,50604,829],{"class":1912},[1736,50606,34907],{"class":1935},[1736,50608,7045],{"class":1912},[1736,50610,50611],{"class":1738,"line":2387},[1736,50612,13147],{"class":1912},[1736,50614,50615],{"class":1738,"line":2393},[1736,50616,1747],{"emptyLinePlaceholder":790},[1736,50618,50619,50621,50623,50625,50627,50629,50631,50633,50635,50637],{"class":1738,"line":2398},[1736,50620,32298],{"class":2674},[1736,50622,7751],{"class":1912},[1736,50624,50115],{"class":1935},[1736,50626,829],{"class":1912},[1736,50628,15790],{"class":4866},[1736,50630,17735],{"class":1912},[1736,50632,25307],{"class":5036},[1736,50634,7778],{"class":1912},[1736,50636,7013],{"class":4866},[1736,50638,4914],{"class":1912},[1736,50640,50641,50643,50645,50647,50649,50651,50653,50655],{"class":1738,"line":2404},[1736,50642,16153],{"class":4866},[1736,50644,22665],{"class":1912},[1736,50646,22668],{"class":2674},[1736,50648,7751],{"class":1912},[1736,50650,50142],{"class":1935},[1736,50652,911],{"class":1912},[1736,50654,10804],{"class":2674},[1736,50656,22680],{"class":1912},[1736,50658,50659,50661,50663,50665,50667,50669,50671,50673,50675],{"class":1738,"line":6959},[1736,50660,16153],{"class":4866},[1736,50662,25447],{"class":2674},[1736,50664,25450],{"class":1912},[1736,50666,22668],{"class":2674},[1736,50668,7751],{"class":1912},[1736,50670,50163],{"class":1935},[1736,50672,32734],{"class":1912},[1736,50674,25473],{"class":2674},[1736,50676,22680],{"class":1912},[1736,50678,50679,50681,50683,50685,50687,50689,50691,50693,50695,50697,50699,50701,50703],{"class":1738,"line":7296},[1736,50680,16153],{"class":4866},[1736,50682,25447],{"class":2674},[1736,50684,25450],{"class":1912},[1736,50686,22668],{"class":2674},[1736,50688,7751],{"class":1912},[1736,50690,49971],{"class":1935},[1736,50692,32734],{"class":1912},[1736,50694,49397],{"class":2674},[1736,50696,7751],{"class":1912},[1736,50698,49980],{"class":1935},[1736,50700,829],{"class":1912},[1736,50702,50196],{"class":1935},[1736,50704,7045],{"class":1912},[1736,50706,50707],{"class":1738,"line":7305},[1736,50708,13147],{"class":1912},[1736,50710,50711],{"class":1738,"line":7310},[1736,50712,1747],{"emptyLinePlaceholder":790},[1736,50714,50715,50718,50720,50722,50724],{"class":1738,"line":9659},[1736,50716,50717],{"class":1912},"  test.",[1736,50719,37119],{"class":2674},[1736,50721,50227],{"class":1912},[1736,50723,34907],{"class":1935},[1736,50725,10691],{"class":1912},[1736,50727,50728],{"class":1738,"line":9680},[1736,50729,1747],{"emptyLinePlaceholder":790},[1736,50731,50732,50734,50736,50738,50740,50742,50744,50746,50748,50750],{"class":1738,"line":9699},[1736,50733,32298],{"class":2674},[1736,50735,7751],{"class":1912},[1736,50737,50244],{"class":1935},[1736,50739,829],{"class":1912},[1736,50741,15790],{"class":4866},[1736,50743,17735],{"class":1912},[1736,50745,25307],{"class":5036},[1736,50747,7778],{"class":1912},[1736,50749,7013],{"class":4866},[1736,50751,4914],{"class":1912},[1736,50753,50754,50756,50758,50760,50762,50764,50766,50768],{"class":1738,"line":9704},[1736,50755,16153],{"class":4866},[1736,50757,22665],{"class":1912},[1736,50759,22668],{"class":2674},[1736,50761,7751],{"class":1912},[1736,50763,50271],{"class":1935},[1736,50765,911],{"class":1912},[1736,50767,10804],{"class":2674},[1736,50769,22680],{"class":1912},[1736,50771,50772,50774,50776],{"class":1738,"line":9715},[1736,50773,16153],{"class":4866},[1736,50775,25447],{"class":2674},[1736,50777,14949],{"class":1912},[1736,50779,50780,50783,50785,50787,50789],{"class":1738,"line":9727},[1736,50781,50782],{"class":1912},"      page.",[1736,50784,22668],{"class":2674},[1736,50786,7751],{"class":1912},[1736,50788,50297],{"class":1935},[1736,50790,7045],{"class":1912},[1736,50792,50793,50796,50798],{"class":1738,"line":9739},[1736,50794,50795],{"class":1912},"    ).",[1736,50797,25473],{"class":2674},[1736,50799,22680],{"class":1912},[1736,50801,50802,50804,50806,50808,50810,50812,50814,50816,50818,50820,50822,50824,50826],{"class":1738,"line":9750},[1736,50803,16153],{"class":4866},[1736,50805,25447],{"class":2674},[1736,50807,25450],{"class":1912},[1736,50809,22668],{"class":2674},[1736,50811,7751],{"class":1912},[1736,50813,49971],{"class":1935},[1736,50815,32734],{"class":1912},[1736,50817,49397],{"class":2674},[1736,50819,7751],{"class":1912},[1736,50821,49980],{"class":1935},[1736,50823,829],{"class":1912},[1736,50825,34907],{"class":1935},[1736,50827,7045],{"class":1912},[1736,50829,50830],{"class":1738,"line":9761},[1736,50831,13147],{"class":1912},[1736,50833,50834],{"class":1738,"line":9767},[1736,50835,10582],{"class":1912},[23,50837,50839],{"id":50838},"checking-it-works","Checking it works",[11,50841,50842],{},"You can run the tests to see if the tests fail but if you want to see the page open while tests are running to visually see the color mode change then I suggest using the VS Code Extension. From there you can select to show browsers and then run the tests from VS Code. Then modify the test to change the color scheme and run the test again to see the page in the browser with the new color scheme.",[22564,50844,5302,50845,5302,50848,22574],{"width":6371,"height":6371,"controls":790},[5304,50846],{"src":50847,"type":22569},"https://res.cloudinary.com/debsobrien/video/upload/f_auto,q_auto/f_auto,q_auto/v1662282584/debbie.codes/blog/2022/color-mode-demo_vsikkn",[5304,50849],{"src":50847,"type":22573},[2011,50851,50852],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":50854},[50855,50856,50857,50858,50859,50860,50861,50862,50863],{"id":15434,"depth":748,"text":15435},{"id":49584,"depth":748,"text":49585},{"id":49654,"depth":748,"text":49655},{"id":49765,"depth":748,"text":49766},{"id":49871,"depth":748,"text":49872},{"id":49998,"depth":748,"text":49999},{"id":50205,"depth":748,"text":50206},{"id":50343,"depth":748,"text":50344},{"id":50838,"depth":748,"text":50839},"2022-09-03","My site has a color mode option to change from light theme to dark theme or sepia theme or use the system preference. So how can we write a test to make sure this all works?","v1662280533/debbie.codes/blog/2022/testing-color-mode_j9i7sj.png",{"ogImage":50868,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1662280533/debbie.codes/blog/2022/testing-color-mode_j9i7sj.png","/blog/testing-color-mode",{"title":49488,"description":50865},"blog/testing-color-mode",[1411,1412],"ov_FDNpEXPEU4gCQEGlbSo4kwMDsbR6tlR6sT97yRGs",{"id":50875,"title":50876,"body":50877,"canonical":788,"date":23734,"description":51275,"extension":786,"featured":787,"image":51276,"meta":51277,"navigation":790,"ogimage":788,"path":51279,"provider":3460,"published":790,"seo":51280,"stem":51281,"tags":51282,"url":788,"__hash__":51283},"blog/blog/testing-iframes-with-playwright.md","Testing iframes with Playwright",{"type":8,"value":50878,"toc":51263},[50879,50886,50891,50894,50900,50903,50909,50915,50919,50922,50945,50948,50954,50958,50961,50988,50992,50999,51023,51027,51034,51085,51089,51096,51150,51153,51159,51165,51169,51172,51178,51187,51221,51224,51226,51229,51231,51260],[11,50880,50881,50882,50885],{},"Have you ever had to test something in an iframe on your page with end to end testing? Even testing the play button of an embedded video used to be difficult but then along came ",[15,50883,36327],{"href":22726,"rel":50884},[19],", reliable end-to-end testing for modern web apps. Let's take a look at how Playwright can help you test iframes.",[11,50887,22542,50888,22548],{},[15,50889,22547],{"href":22545,"rel":50890},[19],[23,50892,26881],{"id":50893},"locators",[11,50895,50896,50897,891],{},"Before we dive into testing iframes let's first understand how Playwright works when it comes to using ",[15,50898,50893],{"href":26879,"rel":50899},[19],[11,50901,50902],{},"A locator is a way to find element(s) on the page at any moment with built in auto-waiting and retry-ability. That means no set timeouts are needed as Playwright will auto wait for the element to appear, including iframes.",[11,50904,50905,50906,10763],{},"Locators are created with the ",[179,50907,50908],{},"page.locator(selector[, options])",[11,50910,50911],{},[121,50912],{"alt":50913,"src":50914},"diagram showing locators in use","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648135568/debbie.codes/blog/2022/locators_2x_kjg4xn.png",[138,50916,50918],{"id":50917},"strict-by-default","Strict by default",[11,50920,50921],{},"Locators are strict by default meaning it will throw an error if it finds more than one.",[299,50923,50925],{"className":8734,"code":50924,"language":8736,"meta":307,"style":307},"await page.locator('button').click()\n",[179,50926,50927],{"__ignoreMap":307},[1736,50928,50929,50931,50933,50935,50937,50939,50941,50943],{"class":1738,"line":1739},[1736,50930,22662],{"class":4866},[1736,50932,22665],{"class":1912},[1736,50934,22668],{"class":2674},[1736,50936,7751],{"class":1912},[1736,50938,10682],{"class":1935},[1736,50940,911],{"class":1912},[1736,50942,10804],{"class":2674},[1736,50944,22680],{"class":1912},[11,50946,50947],{},"So imagine this scenario where we have many buttons on the page. If there is more than one button element on the page then the test will fail as it won't know which button you want it to click on.",[11,50949,50950],{},[121,50951],{"alt":50952,"src":50953},"page showing multiple card components with the same button","https://res.cloudinary.com/debsobrien/image/upload/v1648135760/debbie.codes/blog/2022/home-page-button-example_2x_rrjtm5.png",[138,50955,50957],{"id":50956},"locators-first","Locators + first",[11,50959,50960],{},"There are many ways to fix this but one example is to choose the first instance of the button.",[299,50962,50964],{"className":8734,"code":50963,"language":8736,"meta":307,"style":307},"await page.locator('button').first().click()\n",[179,50965,50966],{"__ignoreMap":307},[1736,50967,50968,50970,50972,50974,50976,50978,50980,50982,50984,50986],{"class":1738,"line":1739},[1736,50969,22662],{"class":4866},[1736,50971,22665],{"class":1912},[1736,50973,22668],{"class":2674},[1736,50975,7751],{"class":1912},[1736,50977,10682],{"class":1935},[1736,50979,911],{"class":1912},[1736,50981,32610],{"class":2674},[1736,50983,16221],{"class":1912},[1736,50985,10804],{"class":2674},[1736,50987,22680],{"class":1912},[138,50989,50991],{"id":50990},"locators-nth-child","Locators + nth-child",[11,50993,50994,50995,50998],{},"Another option is to use the CSS ",[179,50996,50997],{},"nth-child"," selector. This will select the element that is the nth-child of it's parent. In this case the first one.",[299,51000,51002],{"className":8734,"code":51001,"language":8736,"meta":307,"style":307},"await page.locator('div:nth-child(1) button').click()\n",[179,51003,51004],{"__ignoreMap":307},[1736,51005,51006,51008,51010,51012,51014,51017,51019,51021],{"class":1738,"line":1739},[1736,51007,22662],{"class":4866},[1736,51009,22665],{"class":1912},[1736,51011,22668],{"class":2674},[1736,51013,7751],{"class":1912},[1736,51015,51016],{"class":1935},"'div:nth-child(1) button'",[1736,51018,911],{"class":1912},[1736,51020,10804],{"class":2674},[1736,51022,22680],{"class":1912},[138,51024,51026],{"id":51025},"locators-hastext","Locators + hasText",[11,51028,51029,51030,51033],{},"Another options is using ",[179,51031,51032],{},"HasText"," which is case insensitive substring matching and accepts both strings and regular expressions. This means we can search in every div with the class of 'shoes-card' and find the one which has the text 'Guchi' and locate the button inside of it.",[299,51035,51037],{"className":8734,"code":51036,"language":8736,"meta":307,"style":307},"await page\n  .locator('.shoes-card', { hasText: 'Guchi' })\n  .locator('button')\n  .click()\n",[179,51038,51039,51046,51065,51077],{"__ignoreMap":307},[1736,51040,51041,51043],{"class":1738,"line":1739},[1736,51042,22662],{"class":4866},[1736,51044,51045],{"class":1912}," page\n",[1736,51047,51048,51050,51052,51054,51057,51060,51063],{"class":1738,"line":748},[1736,51049,27458],{"class":1912},[1736,51051,22668],{"class":2674},[1736,51053,7751],{"class":1912},[1736,51055,51056],{"class":1935},"'.shoes-card'",[1736,51058,51059],{"class":1912},", { hasText: ",[1736,51061,51062],{"class":1935},"'Guchi'",[1736,51064,10691],{"class":1912},[1736,51066,51067,51069,51071,51073,51075],{"class":1738,"line":756},[1736,51068,27458],{"class":1912},[1736,51070,22668],{"class":2674},[1736,51072,7751],{"class":1912},[1736,51074,10682],{"class":1935},[1736,51076,7045],{"class":1912},[1736,51078,51079,51081,51083],{"class":1738,"line":1755},[1736,51080,27458],{"class":1912},[1736,51082,10804],{"class":2674},[1736,51084,22680],{"class":1912},[138,51086,51088],{"id":51087},"locators-has","Locators + has:",[11,51090,51091,51092,51095],{},"Another option similar to the one above is to use the ",[179,51093,51094],{},"has:"," option which makes sure it contains another locator inside of it.",[299,51097,51099],{"className":8734,"code":51098,"language":8736,"meta":307,"style":307},"await page\n  .locator('.shoes-card', { has: page.locator('text=Guchi') })\n  .locator('button')\n  .click()\n",[179,51100,51101,51107,51130,51142],{"__ignoreMap":307},[1736,51102,51103,51105],{"class":1738,"line":1739},[1736,51104,22662],{"class":4866},[1736,51106,51045],{"class":1912},[1736,51108,51109,51111,51113,51115,51117,51120,51122,51124,51127],{"class":1738,"line":748},[1736,51110,27458],{"class":1912},[1736,51112,22668],{"class":2674},[1736,51114,7751],{"class":1912},[1736,51116,51056],{"class":1935},[1736,51118,51119],{"class":1912},", { has: page.",[1736,51121,22668],{"class":2674},[1736,51123,7751],{"class":1912},[1736,51125,51126],{"class":1935},"'text=Guchi'",[1736,51128,51129],{"class":1912},") })\n",[1736,51131,51132,51134,51136,51138,51140],{"class":1738,"line":756},[1736,51133,27458],{"class":1912},[1736,51135,22668],{"class":2674},[1736,51137,7751],{"class":1912},[1736,51139,10682],{"class":1935},[1736,51141,7045],{"class":1912},[1736,51143,51144,51146,51148],{"class":1738,"line":1755},[1736,51145,27458],{"class":1912},[1736,51147,10804],{"class":2674},[1736,51149,22680],{"class":1912},[11,51151,51152],{},"To show you another example of this in action we have a shopping cart page with items in a cart. We want to select the remove from cart button to make sure we can remove a specific item from the cart.",[11,51154,51155,51156,51158],{},"Again we use the ",[179,51157,51094],{}," option to find the row that has the a locator this time using the image alt selector with the value of 'Guchi'. Our locator now concentrates only inside this one row and locates the button with the aria label of remove from cart. Again we need to be careful of strict mode and ensure we really only have one image alt with the value of 'Guchi' otherwise our test will fail.",[11,51160,51161],{},[121,51162],{"alt":51163,"src":51164},"code example showing a shopping cart rows with an arrow pointing to the button of only one row","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648137581/debbie.codes/blog/2022/has-locator_2x_wlg69v.png",[23,51166,51168],{"id":51167},"frame-locators","Frame Locators",[11,51170,51171],{},"When testing iframes we can use the FrameLocator which allows us to retrieve the iframe and locate elements inside that iframe. For example we could test a play or pause button from a youTube video embedded on your page.",[11,51173,51174],{},[121,51175],{"alt":51176,"src":51177},"page with a youtube iframe and dev tools open showing the code","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1648137047/debbie.codes/blog/2022/iframe_2x_qqetp2.png",[11,51179,51180,51181,10141,51184,10763],{},"FrameLocator can be created with either ",[179,51182,51183],{},"page.frameLocator(selector)",[179,51185,51186],{},"locator.frameLocator(selector)",[299,51188,51190],{"className":8734,"code":51189,"language":8736,"meta":307,"style":307},"await page.frameLocator('iframe').locator('[aria-label=\"Pause\"]').click()\n",[179,51191,51192],{"__ignoreMap":307},[1736,51193,51194,51196,51198,51201,51203,51206,51208,51210,51212,51215,51217,51219],{"class":1738,"line":1739},[1736,51195,22662],{"class":4866},[1736,51197,22665],{"class":1912},[1736,51199,51200],{"class":2674},"frameLocator",[1736,51202,7751],{"class":1912},[1736,51204,51205],{"class":1935},"'iframe'",[1736,51207,911],{"class":1912},[1736,51209,22668],{"class":2674},[1736,51211,7751],{"class":1912},[1736,51213,51214],{"class":1935},"'[aria-label=\"Pause\"]'",[1736,51216,911],{"class":1912},[1736,51218,10804],{"class":2674},[1736,51220,22680],{"class":1912},[11,51222,51223],{},"Just like with locators, Frame locators are also strict so this test will throw an error if it finds more than one iframe on the page.",[23,51225,3294],{"id":3293},[11,51227,51228],{},"And thats how you can use locators and frame locators to test iframes on a page. Have fun testing and as always reach out if you have any questions or comments.",[23,51230,5706],{"id":5705},[70,51232,51233,51238,51243,51248,51255],{},[73,51234,51235],{},[15,51236,22728],{"href":22726,"rel":51237},[19],[73,51239,51240],{},[15,51241,22735],{"href":22733,"rel":51242},[19],[73,51244,51245],{},[15,51246,22742],{"href":22740,"rel":51247},[19],[73,51249,51250],{},[15,51251,51254],{"href":51252,"rel":51253},"https://playwright.dev/docs/api/class-framelocator",[19],"FrameLocator",[73,51256,51257],{},[15,51258,22547],{"href":22545,"rel":51259},[19],[2011,51261,51262],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":51264},[51265,51272,51273,51274],{"id":50893,"depth":748,"text":26881,"children":51266},[51267,51268,51269,51270,51271],{"id":50917,"depth":756,"text":50918},{"id":50956,"depth":756,"text":50957},{"id":50990,"depth":756,"text":50991},{"id":51025,"depth":756,"text":51026},{"id":51087,"depth":756,"text":51088},{"id":51167,"depth":748,"text":51168},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"Have you ever had to test something in an iframe on your page with end to end testing? Even testing the play button of an embedded video used to be difficult but then along came Playwright. Let's take a look at how Playwright can help you test iframes.","v1648137581/debbie.codes/blog/2022/has-locator_2x_wlg69v.png",{"ogImage":51278,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1648137581/debbie.codes/blog/2022/has-locator_2x_wlg69v.png","/blog/testing-iframes-with-playwright",{"title":50876,"description":51275},"blog/testing-iframes-with-playwright",[1411,1412],"rDtiYzk3W4uMEiwhsE5vdAn3j1ise5cmP7dx5ncW_fs",{"id":51285,"title":51286,"body":51287,"canonical":51572,"date":51573,"description":51574,"extension":786,"featured":787,"image":788,"meta":51575,"navigation":790,"ogimage":788,"path":51576,"provider":788,"published":790,"seo":51577,"stem":51578,"tags":51579,"url":788,"__hash__":51580},"blog/blog/testing-logged-in-state-playwright-mcp-browser-extension.md","Testing in a Logged-In State with the Playwright MCP Browser Extension",{"type":8,"value":51288,"toc":51564},[51289,51292,51303,51307,51310,51324,51327,51330,51334,51340,51375,51382,51386,51389,51404,51464,51469,51472,51476,51479,51490,51497,51508,51511,51515,51518,51537,51543,51547,51555,51558,51561],[11,51290,51291],{},"If you've ever needed to test an application that requires authentication, you know the pain: logging in every time or, worse, handing over your credentials to an LLM.",[11,51293,51294,51295,51298,51299,51302],{},"With the new ",[58,51296,51297],{},"Playwright MCP Browser Extension"," for Chrome and Edge, that's no longer necessary. You can now launch the MCP server against an ",[58,51300,51301],{},"existing browser profile"," — one that's already logged in — and test your app.",[23,51304,51306],{"id":51305},"why-this-matters-for-testers","Why This Matters for Testers",[11,51308,51309],{},"Previously, using the Playwright MCP server meant:",[70,51311,51312,51318,51321],{},[73,51313,51314,51315],{},"Starting from a ",[58,51316,51317],{},"clean browser state",[73,51319,51320],{},"Manually logging in every time",[73,51322,51323],{},"Or passing credentials into the LLM (not ideal for security!)",[11,51325,51326],{},"Now, you can:\n✅ Reuse a browser profile you already use for testing\n✅ Start directly in a logged-in session\n✅ Run Playwright MCP commands in that context",[11,51328,51329],{},"This opens up more realistic testing workflows for QA engineers, especially when working with enterprise apps that live behind authentication.",[23,51331,51333],{"id":51332},"installing-the-playwright-mcp-browser-extension","Installing the Playwright MCP Browser Extension",[11,51335,51336,51337,891],{},"The extension is available for ",[58,51338,51339],{},"Chrome, Edge, or any Chromium-based browser",[70,51341,51342,51350,51353,51362,51368],{},[73,51343,51344,51345,891],{},"Download the ",[15,51346,51349],{"href":51347,"rel":51348},"https://github.com/microsoft/playwright-mcp/blob/main/extension/README.md",[19],"Playwright MCP extension",[73,51351,51352],{},"Unzip the file.",[73,51354,2981,51355,10141,51358,51361],{},[179,51356,51357],{},"chrome://extensions",[179,51359,51360],{},"edge://extensions"," in your browser.",[73,51363,51364,51365,891],{},"Enable ",[58,51366,51367],{},"Developer Mode",[73,51369,51370,51371,51374],{},"Click ",[58,51372,51373],{},"Load unpacked"," and select the extension folder.",[11,51376,51377,51378,51381],{},"You'll now see the ",[58,51379,51380],{},"Playwright MCP Bridge"," in your extensions list.",[23,51383,51385],{"id":51384},"configuring-the-mcp-server","Configuring the MCP Server",[11,51387,51388],{},"Next, configure your Playwright MCP server to use the extension.",[70,51390,51391,51394,51401],{},[73,51392,51393],{},"In VS Code, go to the MCP server settings.",[73,51395,51396,51397,51400],{},"Add the ",[179,51398,51399],{},"--extension"," flag to enable extension support.",[73,51402,51403],{},"If you don't have the Playwright MCP extension installed yet, you can do so directly from VS Code.",[299,51405,51407],{"className":1903,"code":51406,"language":1905,"meta":307,"style":307},"\"playwright\": {\n  \"type\": \"stdio\",\n  \"command\": \"npx\",\n  \"args\": [\n    \"@playwright/mcp@latest\",\n    \"--extension\"\n  ],\n},\n",[179,51408,51409,51416,51426,51437,51444,51451,51456,51460],{"__ignoreMap":307},[1736,51410,51411,51414],{"class":1738,"line":1739},[1736,51412,51413],{"class":1935},"\"playwright\"",[1736,51415,1922],{"class":1912},[1736,51417,51418,51420,51422,51424],{"class":1738,"line":748},[1736,51419,17407],{"class":1918},[1736,51421,3065],{"class":1912},[1736,51423,18844],{"class":1935},[1736,51425,1939],{"class":1912},[1736,51427,51428,51431,51433,51435],{"class":1738,"line":756},[1736,51429,51430],{"class":1918},"  \"command\"",[1736,51432,3065],{"class":1912},[1736,51434,18855],{"class":1935},[1736,51436,1939],{"class":1912},[1736,51438,51439,51442],{"class":1738,"line":1755},[1736,51440,51441],{"class":1918},"  \"args\"",[1736,51443,1930],{"class":1912},[1736,51445,51446,51449],{"class":1738,"line":1761},[1736,51447,51448],{"class":1935},"    \"@playwright/mcp@latest\"",[1736,51450,1939],{"class":1912},[1736,51452,51453],{"class":1738,"line":1767},[1736,51454,51455],{"class":1935},"    \"--extension\"\n",[1736,51457,51458],{"class":1738,"line":1772},[1736,51459,33299],{"class":1912},[1736,51461,51462],{"class":1738,"line":1778},[1736,51463,16366],{"class":1912},[11,51465,51466,51468],{},[58,51467,39068],{},": In VS Code from the extensions tab you can install Playwright MCP server and then use the gear icon to choose the \"show configuration JSON\" option to easily add \"--extension\".",[11,51470,51471],{},"That's it — your MCP server is now aware of the extension.",[23,51473,51475],{"id":51474},"testing-on-a-logged-in-profile","Testing on a Logged-In Profile",[11,51477,51478],{},"Here's the exciting part:",[70,51480,51481,51484,51487],{},[73,51482,51483],{},"Open your browser on the profile you want to use and login to the site you want to test (e.g. GitHub).",[73,51485,51486],{},"Start your MCP server and ask it to Navigate to a site.",[73,51488,51489],{},"Choose the tab you want the MCP Server to connect to.",[11,51491,51492,51493,51496],{},"From there, you can instruct Copilot in Agent mode to interact with your app in a ",[58,51494,51495],{},"logged-in state",". For example:",[70,51498,51499,51502,51505],{},[73,51500,51501],{},"Navigating to your profile page",[73,51503,51504],{},"Changing account settings",[73,51506,51507],{},"Generating tests for authenticated user flows",[11,51509,51510],{},"In my demo, I updated GitHub profile pronouns — and MCP handled it seamlessly from the logged-in session.",[23,51512,51514],{"id":51513},"why-this-is-a-game-changer","Why This is a Game-Changer",[11,51516,51517],{},"This extension makes it possible to:",[70,51519,51520,51527,51530],{},[73,51521,51522,51523,51526],{},"Test ",[58,51524,51525],{},"real user sessions"," without worrying about logging-in",[73,51528,51529],{},"Run automation against enterprise apps that require authentication",[73,51531,51532,51533,51536],{},"Keep your login credentials ",[58,51534,51535],{},"safe"," by staying within your own profile",[11,51538,51539,51540,891],{},"For testers, this means ",[58,51541,51542],{},"faster setup, more realistic tests, and improved security",[23,51544,51546],{"id":51545},"get-started","Get Started",[11,51548,51549,51550,51554],{},"👉 Check out the ",[15,51551,51553],{"href":51347,"rel":51552},[19],"Playwright MCP Browser Extension docs"," and try it out for yourself.",[11,51556,51557],{},"I'd love to hear how you use logged-in profiles in your testing workflows. Drop a comment and share your scenarios!",[5932,51559],{"width":5934,"height":5935,"src":51560,"title":30222,"frameBorder":1290,"allow":5938,"allowFullScreen":790},"https://www.youtube.com/embed/uE0r51pneSA",[2011,51562,51563],{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":51565},[51566,51567,51568,51569,51570,51571],{"id":51305,"depth":748,"text":51306},{"id":51332,"depth":748,"text":51333},{"id":51384,"depth":748,"text":51385},{"id":51474,"depth":748,"text":51475},{"id":51513,"depth":748,"text":51514},{"id":51545,"depth":748,"text":51546},"https://dev.to/debs_obrien/testing-in-a-logged-in-state-with-the-playwright-mcp-browser-extension-4cmg","2025-08-21","If you've ever needed to test an application that requires authentication, you know the pain of logging in every time or, worse, handing over your credentials to an LLM. With the new Playwright MCP Browser Extension for Chrome and Edge, that's no longer necessary.",{},"/blog/testing-logged-in-state-playwright-mcp-browser-extension",{"title":51286,"description":51574},"blog/testing-logged-in-state-playwright-mcp-browser-extension",[1412,3321,1411,795],"qX9h5q9CAGSjIde80L_G8OxRnB6gLl8CMOt6G4fuBK4",{"id":51582,"title":51583,"body":51584,"canonical":788,"date":52511,"description":52512,"extension":786,"featured":787,"image":52513,"meta":52514,"navigation":790,"ogimage":788,"path":52516,"provider":5235,"published":790,"seo":52517,"stem":52518,"tags":52519,"url":788,"__hash__":52520},"blog/blog/testing-menus-desktop-mobile.md","Testing Menus on Desktop and Mobile",{"type":8,"value":51585,"toc":52501},[51586,51593,51597,51612,51618,51711,51725,51838,51842,51845,52010,52017,52021,52024,52125,52135,52138,52142,52159,52199,52203,52225,52467,52470,52477,52479,52498],[11,51587,51588,51589,51592],{},"We normally all have a menu on our website so we can link from one page to another. For example on my website the menu links to the about page, videos page, podcasts page, courses page and blog page. We can write a simple test to make sure that when a user clicks on an item in the menu it takes them to the correct page. To test my website I use ",[15,51590,36327],{"href":22726,"rel":51591},[19]," which I already have installed. Let's take a look at how to test the menu on desktop and mobile.",[23,51594,51596],{"id":51595},"testing-the-menu-on-desktop","Testing the menu on desktop",[11,51598,51599,51600,51604,51605,51608,51609,891],{},"Playwright has a great way of easily getting started with testing by using the test generator. I normally use the ",[15,51601,51603],{"href":22740,"rel":51602},[19],"Playwright VS Code extension"," to generate my tests. Then I simply open the the testing sidebar and click on record new. This will create a ",[179,51606,51607],{},"test-1.spec.ts"," file which I can later rename to ",[179,51610,51611],{},"navigation.spec.ts",[11,51613,51614,51615,51617],{},"By clicking on the record new button a browser window will appear where I can add in the URL that I want to test which in this case is ",[179,51616,37694],{},". Playwright will record your user actions and generate the test code for you so basically in the browser window you can click on the about link in the menu and it will generate the code for you.",[299,51619,51621],{"className":8734,"code":51620,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test';\n\ntest(`test`, async ({ page }) => {\n  await page.goto('/debbie.codes');\n  await page.getByRole('navigation').getByRole('link', { name: 'About' }).click();\n})\n",[179,51622,51623,51635,51639,51662,51677,51707],{"__ignoreMap":307},[1736,51624,51625,51627,51629,51631,51633],{"class":1738,"line":1739},[1736,51626,4996],{"class":4866},[1736,51628,49534],{"class":1912},[1736,51630,5002],{"class":4866},[1736,51632,25282],{"class":1935},[1736,51634,7682],{"class":1912},[1736,51636,51637],{"class":1738,"line":748},[1736,51638,1747],{"emptyLinePlaceholder":790},[1736,51640,51641,51643,51645,51648,51650,51652,51654,51656,51658,51660],{"class":1738,"line":756},[1736,51642,25293],{"class":2674},[1736,51644,7751],{"class":1912},[1736,51646,51647],{"class":1935},"`test`",[1736,51649,829],{"class":1912},[1736,51651,15790],{"class":4866},[1736,51653,17735],{"class":1912},[1736,51655,25307],{"class":5036},[1736,51657,7778],{"class":1912},[1736,51659,7013],{"class":4866},[1736,51661,4914],{"class":1912},[1736,51663,51664,51666,51668,51670,51672,51675],{"class":1738,"line":1755},[1736,51665,25323],{"class":4866},[1736,51667,22665],{"class":1912},[1736,51669,25328],{"class":2674},[1736,51671,7751],{"class":1912},[1736,51673,51674],{"class":1935},"'/debbie.codes'",[1736,51676,16106],{"class":1912},[1736,51678,51679,51681,51683,51685,51687,51689,51691,51693,51695,51697,51699,51701,51703,51705],{"class":1738,"line":1761},[1736,51680,25323],{"class":4866},[1736,51682,22665],{"class":1912},[1736,51684,1032],{"class":2674},[1736,51686,7751],{"class":1912},[1736,51688,27355],{"class":1935},[1736,51690,911],{"class":1912},[1736,51692,1032],{"class":2674},[1736,51694,7751],{"class":1912},[1736,51696,27009],{"class":1935},[1736,51698,10685],{"class":1912},[1736,51700,27047],{"class":1935},[1736,51702,25393],{"class":1912},[1736,51704,10804],{"class":2674},[1736,51706,15752],{"class":1912},[1736,51708,51709],{"class":1738,"line":1767},[1736,51710,10582],{"class":1912},[11,51712,51713,51714,51716,51717,30730,51719,51722,51723,2900],{},"From here we can simply modify the test to actually assert something. For example we can assert that the URL is correct once the click action has happened. We can also change the title and modify the ",[179,51715,49521],{}," to just be ",[179,51718,1066],{},[179,51720,51721],{},"/debbie.codes"," as we have the base URL set in the ",[179,51724,25689],{},[299,51726,51728],{"className":8734,"code":51727,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test';\n\ntest(`menu links to about page`, async ({ page }) => {\n  await page.goto('/');\n  await page.getByRole('navigation').getByRole('link', { name: 'About' }).click();\n  await expect(page).toHaveURL(/about/);\n})\n",[179,51729,51730,51742,51746,51769,51783,51813,51834],{"__ignoreMap":307},[1736,51731,51732,51734,51736,51738,51740],{"class":1738,"line":1739},[1736,51733,4996],{"class":4866},[1736,51735,49534],{"class":1912},[1736,51737,5002],{"class":4866},[1736,51739,25282],{"class":1935},[1736,51741,7682],{"class":1912},[1736,51743,51744],{"class":1738,"line":748},[1736,51745,1747],{"emptyLinePlaceholder":790},[1736,51747,51748,51750,51752,51755,51757,51759,51761,51763,51765,51767],{"class":1738,"line":756},[1736,51749,25293],{"class":2674},[1736,51751,7751],{"class":1912},[1736,51753,51754],{"class":1935},"`menu links to about page`",[1736,51756,829],{"class":1912},[1736,51758,15790],{"class":4866},[1736,51760,17735],{"class":1912},[1736,51762,25307],{"class":5036},[1736,51764,7778],{"class":1912},[1736,51766,7013],{"class":4866},[1736,51768,4914],{"class":1912},[1736,51770,51771,51773,51775,51777,51779,51781],{"class":1738,"line":1755},[1736,51772,25323],{"class":4866},[1736,51774,22665],{"class":1912},[1736,51776,25328],{"class":2674},[1736,51778,7751],{"class":1912},[1736,51780,21291],{"class":1935},[1736,51782,16106],{"class":1912},[1736,51784,51785,51787,51789,51791,51793,51795,51797,51799,51801,51803,51805,51807,51809,51811],{"class":1738,"line":1761},[1736,51786,25323],{"class":4866},[1736,51788,22665],{"class":1912},[1736,51790,1032],{"class":2674},[1736,51792,7751],{"class":1912},[1736,51794,27355],{"class":1935},[1736,51796,911],{"class":1912},[1736,51798,1032],{"class":2674},[1736,51800,7751],{"class":1912},[1736,51802,27009],{"class":1935},[1736,51804,10685],{"class":1912},[1736,51806,27047],{"class":1935},[1736,51808,25393],{"class":1912},[1736,51810,10804],{"class":2674},[1736,51812,15752],{"class":1912},[1736,51814,51815,51817,51819,51821,51824,51826,51828,51830,51832],{"class":1738,"line":1767},[1736,51816,25323],{"class":4866},[1736,51818,25447],{"class":2674},[1736,51820,32454],{"class":1912},[1736,51822,51823],{"class":2674},"toHaveURL",[1736,51825,7751],{"class":1912},[1736,51827,1066],{"class":1935},[1736,51829,27085],{"class":13136},[1736,51831,1066],{"class":1935},[1736,51833,16106],{"class":1912},[1736,51835,51836],{"class":1738,"line":1772},[1736,51837,10582],{"class":1912},[138,51839,51841],{"id":51840},"creating-a-loop","Creating a loop",[11,51843,51844],{},"We could repeat this whole process for each link in the menu. However, we can make this a lot easier by using a loop. We can create an array of the links we want to test and then loop over them. This way we can test all the links in the menu with just a few lines of code.",[299,51846,51848],{"className":8734,"code":51847,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test';\n\nconst links = ['about', 'videos', 'podcasts', 'blog', 'courses']\n\ntest('menu links to correct page', async ({ page }) => {\n  await page.goto('/');\n\n  for (const link of links) {\n    await page.getByRole('navigation').getByRole('link', { name: link }).click();\n    await expect(page).toHaveURL(link);\n  }\n})\n",[179,51849,51850,51862,51866,51899,51903,51926,51940,51944,51962,51989,52002,52006],{"__ignoreMap":307},[1736,51851,51852,51854,51856,51858,51860],{"class":1738,"line":1739},[1736,51853,4996],{"class":4866},[1736,51855,49534],{"class":1912},[1736,51857,5002],{"class":4866},[1736,51859,25282],{"class":1935},[1736,51861,7682],{"class":1912},[1736,51863,51864],{"class":1738,"line":748},[1736,51865,1747],{"emptyLinePlaceholder":790},[1736,51867,51868,51870,51873,51875,51877,51880,51882,51884,51886,51888,51890,51892,51894,51897],{"class":1738,"line":756},[1736,51869,5029],{"class":4866},[1736,51871,51872],{"class":1918}," links",[1736,51874,4911],{"class":4866},[1736,51876,8409],{"class":1912},[1736,51878,51879],{"class":1935},"'about'",[1736,51881,829],{"class":1912},[1736,51883,33632],{"class":1935},[1736,51885,829],{"class":1912},[1736,51887,33741],{"class":1935},[1736,51889,829],{"class":1912},[1736,51891,33378],{"class":1935},[1736,51893,829],{"class":1912},[1736,51895,51896],{"class":1935},"'courses'",[1736,51898,8420],{"class":1912},[1736,51900,51901],{"class":1738,"line":1755},[1736,51902,1747],{"emptyLinePlaceholder":790},[1736,51904,51905,51907,51909,51912,51914,51916,51918,51920,51922,51924],{"class":1738,"line":1761},[1736,51906,25293],{"class":2674},[1736,51908,7751],{"class":1912},[1736,51910,51911],{"class":1935},"'menu links to correct page'",[1736,51913,829],{"class":1912},[1736,51915,15790],{"class":4866},[1736,51917,17735],{"class":1912},[1736,51919,25307],{"class":5036},[1736,51921,7778],{"class":1912},[1736,51923,7013],{"class":4866},[1736,51925,4914],{"class":1912},[1736,51927,51928,51930,51932,51934,51936,51938],{"class":1738,"line":1767},[1736,51929,25323],{"class":4866},[1736,51931,22665],{"class":1912},[1736,51933,25328],{"class":2674},[1736,51935,7751],{"class":1912},[1736,51937,21291],{"class":1935},[1736,51939,16106],{"class":1912},[1736,51941,51942],{"class":1738,"line":1772},[1736,51943,1747],{"emptyLinePlaceholder":790},[1736,51945,51946,51949,51951,51953,51956,51959],{"class":1738,"line":1778},[1736,51947,51948],{"class":4866},"  for",[1736,51950,1095],{"class":1912},[1736,51952,5029],{"class":4866},[1736,51954,51955],{"class":1918}," link",[1736,51957,51958],{"class":4866}," of",[1736,51960,51961],{"class":1912}," links) {\n",[1736,51963,51964,51966,51968,51970,51972,51974,51976,51978,51980,51982,51985,51987],{"class":1738,"line":1784},[1736,51965,16153],{"class":4866},[1736,51967,22665],{"class":1912},[1736,51969,1032],{"class":2674},[1736,51971,7751],{"class":1912},[1736,51973,27355],{"class":1935},[1736,51975,911],{"class":1912},[1736,51977,1032],{"class":2674},[1736,51979,7751],{"class":1912},[1736,51981,27009],{"class":1935},[1736,51983,51984],{"class":1912},", { name: link }).",[1736,51986,10804],{"class":2674},[1736,51988,15752],{"class":1912},[1736,51990,51991,51993,51995,51997,51999],{"class":1738,"line":1790},[1736,51992,16153],{"class":4866},[1736,51994,25447],{"class":2674},[1736,51996,32454],{"class":1912},[1736,51998,51823],{"class":2674},[1736,52000,52001],{"class":1912},"(link);\n",[1736,52003,52004],{"class":1738,"line":1796},[1736,52005,1971],{"class":1912},[1736,52007,52008],{"class":1738,"line":2353},[1736,52009,10582],{"class":1912},[11,52011,52012,52013,52016],{},"We now have a very simple test that will test all the links in the menu. We can run this test by clicking on the play button in the testing sidebar. This will run the test with the browser opened once we have ",[58,52014,52015],{},"show browser"," checked in our testing sidebar in VS Code.",[23,52018,52020],{"id":52019},"testing-the-menu-on-mobile","Testing the menu on mobile",[11,52022,52023],{},"So what happens when we want to run this test on mobile? Setting up testing on mobile viewports is easily done through the Playwright config. There are two examples in the projects array already commented out so we can just uncomment them. This means our tests will now run on these mobile viewports as well as the default desktop browsers.",[299,52025,52027],{"className":8734,"code":52026,"language":8736,"meta":307,"style":307},"projects: [\n  // ...\n  /* Test against mobile viewports. */\n  {\n    name: 'Mobile Chrome',\n    use: {\n      ...devices['Pixel 5'],\n    },\n  },\n  {\n    name: 'Mobile Safari',\n    use: {\n      ...devices['iPhone 12'],\n    },\n  },\n]\n",[179,52028,52029,52036,52041,52046,52050,52059,52064,52077,52081,52085,52089,52098,52102,52113,52117,52121],{"__ignoreMap":307},[1736,52030,52031,52034],{"class":1738,"line":1739},[1736,52032,52033],{"class":2674},"projects",[1736,52035,1930],{"class":1912},[1736,52037,52038],{"class":1738,"line":748},[1736,52039,52040],{"class":6820},"  // ...\n",[1736,52042,52043],{"class":1738,"line":756},[1736,52044,52045],{"class":6820},"  /* Test against mobile viewports. */\n",[1736,52047,52048],{"class":1738,"line":1755},[1736,52049,17704],{"class":1912},[1736,52051,52052,52054,52057],{"class":1738,"line":1761},[1736,52053,31037],{"class":1912},[1736,52055,52056],{"class":1935},"'Mobile Chrome'",[1736,52058,1939],{"class":1912},[1736,52060,52061],{"class":1738,"line":1767},[1736,52062,52063],{"class":1912},"    use: {\n",[1736,52065,52066,52069,52072,52075],{"class":1738,"line":1772},[1736,52067,52068],{"class":4866},"      ...",[1736,52070,52071],{"class":1912},"devices[",[1736,52073,52074],{"class":1935},"'Pixel 5'",[1736,52076,16460],{"class":1912},[1736,52078,52079],{"class":1738,"line":1778},[1736,52080,8553],{"class":1912},[1736,52082,52083],{"class":1738,"line":1784},[1736,52084,4929],{"class":1912},[1736,52086,52087],{"class":1738,"line":1790},[1736,52088,17704],{"class":1912},[1736,52090,52091,52093,52096],{"class":1738,"line":1796},[1736,52092,31037],{"class":1912},[1736,52094,52095],{"class":1935},"'Mobile Safari'",[1736,52097,1939],{"class":1912},[1736,52099,52100],{"class":1738,"line":2353},[1736,52101,52063],{"class":1912},[1736,52103,52104,52106,52108,52111],{"class":1738,"line":2358},[1736,52105,52068],{"class":4866},[1736,52107,52071],{"class":1912},[1736,52109,52110],{"class":1935},"'iPhone 12'",[1736,52112,16460],{"class":1912},[1736,52114,52115],{"class":1738,"line":2364},[1736,52116,8553],{"class":1912},[1736,52118,52119],{"class":1738,"line":2370},[1736,52120,4929],{"class":1912},[1736,52122,52123],{"class":1738,"line":2376},[1736,52124,8420],{"class":1912},[11,52126,2506,52127,52130,52131,52134],{},[15,52128,51603],{"href":22740,"rel":52129},[19]," runs on 'Chromium' by default but we can change this to run on one of our mobile projects by clicking on the arrow next to the play button at the top of the testing sidebar and then clicking on ",[58,52132,52133],{},"select the default profile",". Here we can choose 'Mobile Safari'. Now when we run our test in VS Code it will use the mobile Safari browser.",[11,52136,52137],{},"When we run the test we will see that the test fails. This is because the menu is hidden on mobile viewports and we need to click on the hamburger menu to open and see the navigation links. So how do we tell Playwright to click on the hamburger if the viewport is mobile?",[138,52139,52141],{"id":52140},"ismobile-fixture","isMobile Fixture",[11,52143,52144,52145,52148,52149,52151,52152,52154,52155,52158],{},"We can use the ",[179,52146,52147],{},"isMobile"," fixture, first passing it into our test function beside the ",[179,52150,25307],{}," fixture. We can the write an if statement to check if the viewport is mobile and if it is then we can click on the hamburger menu. To find the right locator for the hamburger menu we can either use the ",[58,52153,27302],{}," option or we can use the ",[58,52156,52157],{},"record from cursor"," which will record us clicking ont the hamburger menu and generate the locator for us right where our cursor is located in the test.",[299,52160,52162],{"className":8734,"code":52161,"language":8736,"meta":307,"style":307},"if(isMobile){\n  await page.getByRole('button', { name: 'open menu' }).click();\n}\n",[179,52163,52164,52172,52195],{"__ignoreMap":307},[1736,52165,52166,52169],{"class":1738,"line":1739},[1736,52167,52168],{"class":4866},"if",[1736,52170,52171],{"class":1912},"(isMobile){\n",[1736,52173,52174,52176,52178,52180,52182,52184,52186,52189,52191,52193],{"class":1738,"line":748},[1736,52175,25323],{"class":4866},[1736,52177,22665],{"class":1912},[1736,52179,1032],{"class":2674},[1736,52181,7751],{"class":1912},[1736,52183,10682],{"class":1935},[1736,52185,10685],{"class":1912},[1736,52187,52188],{"class":1935},"'open menu'",[1736,52190,25393],{"class":1912},[1736,52192,10804],{"class":2674},[1736,52194,15752],{"class":1912},[1736,52196,52197],{"class":1738,"line":756},[1736,52198,1976],{"class":1912},[138,52200,52202],{"id":52201},"adding-a-describe-block-and-beforeeach-block","Adding a describe block and beforeEach block",[11,52204,52205,52206,52208,52209,52211,52212,52214,52215,52217,52218,52220,52221,52224],{},"We can tidy up the code a little by adding a ",[179,52207,17717],{}," block and a ",[179,52210,10597],{}," block. The ",[179,52213,17717],{}," block will group all the tests together, we can later add more for footer navigation, and the ",[179,52216,10597],{}," block will run before each test. We can then move the ",[179,52219,52168],{}," statement into the ",[179,52222,52223],{},"for"," loop so that it runs for each link.",[299,52226,52228],{"className":8734,"code":52227,"language":8736,"meta":307,"style":307},"import { expect, test } from '@playwright/test';\n\ntest.beforeEach(async ({ page }) => {\n  await page.goto('/');\n});\n\ntest.describe('navigation', () => {\n\n  const links = ['about', 'videos', 'podcasts', 'blog', 'courses']\n\n  test(`header nav links to correct pages`, async ({ page, isMobile }) => {\n    for (const link of links) {\n      if(isMobile){\n        await page.getByRole('button', { name: 'open menu' }).click();\n      }\n        await page.getByRole('navigation').getByRole('link', { name: link }).click();\n        await expect(page).toHaveURL(link);\n    }\n  })\n})\n",[179,52229,52230,52242,52246,52266,52280,52284,52288,52304,52308,52338,52342,52369,52384,52390,52413,52417,52443,52455,52459,52463],{"__ignoreMap":307},[1736,52231,52232,52234,52236,52238,52240],{"class":1738,"line":1739},[1736,52233,4996],{"class":4866},[1736,52235,49534],{"class":1912},[1736,52237,5002],{"class":4866},[1736,52239,25282],{"class":1935},[1736,52241,7682],{"class":1912},[1736,52243,52244],{"class":1738,"line":748},[1736,52245,1747],{"emptyLinePlaceholder":790},[1736,52247,52248,52250,52252,52254,52256,52258,52260,52262,52264],{"class":1738,"line":756},[1736,52249,32280],{"class":1912},[1736,52251,10597],{"class":2674},[1736,52253,7751],{"class":1912},[1736,52255,15790],{"class":4866},[1736,52257,17735],{"class":1912},[1736,52259,25307],{"class":5036},[1736,52261,7778],{"class":1912},[1736,52263,7013],{"class":4866},[1736,52265,4914],{"class":1912},[1736,52267,52268,52270,52272,52274,52276,52278],{"class":1738,"line":1755},[1736,52269,25323],{"class":4866},[1736,52271,22665],{"class":1912},[1736,52273,25328],{"class":2674},[1736,52275,7751],{"class":1912},[1736,52277,21291],{"class":1935},[1736,52279,16106],{"class":1912},[1736,52281,52282],{"class":1738,"line":1761},[1736,52283,17657],{"class":1912},[1736,52285,52286],{"class":1738,"line":1767},[1736,52287,1747],{"emptyLinePlaceholder":790},[1736,52289,52290,52292,52294,52296,52298,52300,52302],{"class":1738,"line":1772},[1736,52291,32280],{"class":1912},[1736,52293,17717],{"class":2674},[1736,52295,7751],{"class":1912},[1736,52297,27355],{"class":1935},[1736,52299,10524],{"class":1912},[1736,52301,7013],{"class":4866},[1736,52303,4914],{"class":1912},[1736,52305,52306],{"class":1738,"line":1778},[1736,52307,1747],{"emptyLinePlaceholder":790},[1736,52309,52310,52312,52314,52316,52318,52320,52322,52324,52326,52328,52330,52332,52334,52336],{"class":1738,"line":1784},[1736,52311,7824],{"class":4866},[1736,52313,51872],{"class":1918},[1736,52315,4911],{"class":4866},[1736,52317,8409],{"class":1912},[1736,52319,51879],{"class":1935},[1736,52321,829],{"class":1912},[1736,52323,33632],{"class":1935},[1736,52325,829],{"class":1912},[1736,52327,33741],{"class":1935},[1736,52329,829],{"class":1912},[1736,52331,33378],{"class":1935},[1736,52333,829],{"class":1912},[1736,52335,51896],{"class":1935},[1736,52337,8420],{"class":1912},[1736,52339,52340],{"class":1738,"line":1790},[1736,52341,1747],{"emptyLinePlaceholder":790},[1736,52343,52344,52346,52348,52351,52353,52355,52357,52359,52361,52363,52365,52367],{"class":1738,"line":1796},[1736,52345,32298],{"class":2674},[1736,52347,7751],{"class":1912},[1736,52349,52350],{"class":1935},"`header nav links to correct pages`",[1736,52352,829],{"class":1912},[1736,52354,15790],{"class":4866},[1736,52356,17735],{"class":1912},[1736,52358,25307],{"class":5036},[1736,52360,829],{"class":1912},[1736,52362,52147],{"class":5036},[1736,52364,7778],{"class":1912},[1736,52366,7013],{"class":4866},[1736,52368,4914],{"class":1912},[1736,52370,52371,52374,52376,52378,52380,52382],{"class":1738,"line":2353},[1736,52372,52373],{"class":4866},"    for",[1736,52375,1095],{"class":1912},[1736,52377,5029],{"class":4866},[1736,52379,51955],{"class":1918},[1736,52381,51958],{"class":4866},[1736,52383,51961],{"class":1912},[1736,52385,52386,52388],{"class":1738,"line":2358},[1736,52387,14351],{"class":4866},[1736,52389,52171],{"class":1912},[1736,52391,52392,52395,52397,52399,52401,52403,52405,52407,52409,52411],{"class":1738,"line":2364},[1736,52393,52394],{"class":4866},"        await",[1736,52396,22665],{"class":1912},[1736,52398,1032],{"class":2674},[1736,52400,7751],{"class":1912},[1736,52402,10682],{"class":1935},[1736,52404,10685],{"class":1912},[1736,52406,52188],{"class":1935},[1736,52408,25393],{"class":1912},[1736,52410,10804],{"class":2674},[1736,52412,15752],{"class":1912},[1736,52414,52415],{"class":1738,"line":2370},[1736,52416,14448],{"class":1912},[1736,52418,52419,52421,52423,52425,52427,52429,52431,52433,52435,52437,52439,52441],{"class":1738,"line":2376},[1736,52420,52394],{"class":4866},[1736,52422,22665],{"class":1912},[1736,52424,1032],{"class":2674},[1736,52426,7751],{"class":1912},[1736,52428,27355],{"class":1935},[1736,52430,911],{"class":1912},[1736,52432,1032],{"class":2674},[1736,52434,7751],{"class":1912},[1736,52436,27009],{"class":1935},[1736,52438,51984],{"class":1912},[1736,52440,10804],{"class":2674},[1736,52442,15752],{"class":1912},[1736,52444,52445,52447,52449,52451,52453],{"class":1738,"line":2381},[1736,52446,52394],{"class":4866},[1736,52448,25447],{"class":2674},[1736,52450,32454],{"class":1912},[1736,52452,51823],{"class":2674},[1736,52454,52001],{"class":1912},[1736,52456,52457],{"class":1738,"line":2387},[1736,52458,9853],{"class":1912},[1736,52460,52461],{"class":1738,"line":2393},[1736,52462,13147],{"class":1912},[1736,52464,52465],{"class":1738,"line":2398},[1736,52466,10582],{"class":1912},[11,52468,52469],{},"Now clicking on the play button we will see the test passes on mobile. To now check it works on desktop we can either change the default profile back to 'Chromium' or select all profiles and run the test again which will now run it on both mobile and desktop.",[11,52471,52472,52473,52476],{},"Normally when I want to run tests on all projects I will use the command line. ",[179,52474,52475],{},"npx playwright test"," which will run my tests in headless mode meaning I won't see a browser window open. This can be a little quicker when developing and it will give me a nice report at the end of it so I can investigate if one of the tests failed.",[23,52478,5706],{"id":5705},[70,52480,52481,52486,52491],{},[73,52482,52483],{},[15,52484,22728],{"href":22726,"rel":52485},[19],[73,52487,52488],{},[15,52489,51603],{"href":22740,"rel":52490},[19],[73,52492,52493],{},[15,52494,52497],{"href":52495,"rel":52496},"https://github.com/debs-obrien/debbie.codes/blob/master/tests/navigation.spec.ts",[19],"Navigation Test file",[2011,52499,52500],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":52502},[52503,52506,52510],{"id":51595,"depth":748,"text":51596,"children":52504},[52505],{"id":51840,"depth":756,"text":51841},{"id":52019,"depth":748,"text":52020,"children":52507},[52508,52509],{"id":52140,"depth":756,"text":52141},{"id":52201,"depth":756,"text":52202},{"id":5705,"depth":748,"text":5706},"2023-03-01","We can write a simple test to make sure that when a user clicks on an item in the menu it takes them to the correct page. To test my website I use Playwright which I already have installed. Let's take a look at how to test the menu on desktop and mobile.","photo-1510267413785-9d9e64460cde?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&q=80",{"ogImage":52515},"https://unsplash.com/photo-1510267413785-9d9e64460cde?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&q=80","/blog/testing-menus-desktop-mobile",{"title":51583,"description":52512},"blog/testing-menus-desktop-mobile",[1412,1411],"q22AQwjXWYwHmDRwZBp1DKX7Ve5aRTaIYCaMB1nGU_o",{"id":52522,"title":52523,"body":52524,"canonical":788,"date":52528,"description":5505,"extension":786,"featured":787,"image":52529,"meta":52530,"navigation":790,"ogimage":788,"path":52531,"provider":3460,"published":787,"seo":52532,"stem":52533,"tags":52534,"url":52535,"__hash__":52536},"blog/blog/testing-staging-production-envs-playwright.md","Testing Staging and Production environments in Playwright",{"type":8,"value":52525,"toc":52526},[],{"title":307,"searchDepth":748,"depth":748,"links":52527},[],"2024-08-02","v1725567832/debbie.codes/blog/2024/staging-production_fivhwv",{"platform":5508},"/blog/testing-staging-production-envs-playwright",{"title":52523,"description":5505},"blog/testing-staging-production-envs-playwright",[1412,1411],"https://dev.to/playwright/testing-staging-and-production-environments-in-playwright-3p8b","L1Biq2XfoEU5_EKpTENiL-rpxlkrAmIBX4o3NofMH9g",{"id":52538,"title":52539,"body":52540,"canonical":788,"date":53567,"description":52544,"extension":786,"featured":787,"image":53568,"meta":53569,"navigation":790,"ogimage":788,"path":53571,"provider":5235,"published":790,"seo":53572,"stem":53573,"tags":53574,"url":788,"__hash__":53575},"blog/blog/theming-in-react.md","Theming in React",{"type":8,"value":52541,"toc":53555},[52542,52545,52551,52554,52557,52560,52563,52567,52579,52635,52638,52646,52696,52705,52735,52763,52819,52822,52841,52845,52850,52867,52872,52973,52977,52980,52984,52989,53006,53014,53084,53087,53101,53158,53161,53173,53188,53204,53208,53213,53260,53263,53331,53335,53338,53402,53406,53409,53510,53512,53515,53552],[11,52543,52544],{},"Theming is a fascinating topic and a really important one these days as more and more people add light and dark mode to their sites and companies change the theme of their site or part of their site on specific dates to celebrate occasions such as christmas, pride week etc.",[11,52546,52547],{},[121,52548],{"alt":52549,"src":52550},"color cards showing different colors and tones","https://images.unsplash.com/photo-1595446472721-4b7aa63a2bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2370&q=80",[11,52552,52553],{},"But where do we start when it comes to creating themes? It all starts with the designers. They are the ones who are creating the themes. Each theme should have a list of design tokens that show the theme colors, paddings, margins, fonts, etc. These tokens can be exported from tools such as Figma and then used in a theme component.",[11,52555,52556],{},"In this example I will show you how to do theming in React using Bit, however if you are not using Bit this will still work in any React app and you can install the one Bit component that we are using though npm or yarn. The Theme Provider component from teambit will convert these design tokens from JSON format to usable CSS variables without you having to do any extra work, which is kinda cool.",[11,52558,52559],{},"By using Bit we are making sure our components are fully independent and can be easily installed into any app. If you are not using Bit then you will need to change the import statements to use relative paths to your components. And if you are not using typescript then feel free to change the file extension to .jsx and ignore the schema which is just for type checking.",[11,52561,52562],{},"So lets get started with theming in React.",[23,52564,52566],{"id":52565},"step-1-create-your-theme-provider-component","Step 1: Create your Theme Provider Component",[2260,52568,52569,52572],{},[73,52570,52571],{},"Create a new theme provider component.",[73,52573,52574,52575,52578],{},"Add a ",[179,52576,52577],{},"theme-schema.tsx"," file and include all types for your design tokens.",[299,52580,52582],{"className":8734,"code":52581,"language":8736,"meta":307,"style":307},"export interface ThemeSchema {\n  myColorText?: string\n  myFontSize?: string\n  myBorderColor?: string\n  myBorderRadius?: string\n}\n",[179,52583,52584,52595,52604,52613,52622,52631],{"__ignoreMap":307},[1736,52585,52586,52588,52590,52593],{"class":1738,"line":1739},[1736,52587,6632],{"class":4866},[1736,52589,13673],{"class":4866},[1736,52591,52592],{"class":2674}," ThemeSchema",[1736,52594,4914],{"class":1912},[1736,52596,52597,52600,52602],{"class":1738,"line":748},[1736,52598,52599],{"class":5036},"  myColorText",[1736,52601,8830],{"class":4866},[1736,52603,6866],{"class":1918},[1736,52605,52606,52609,52611],{"class":1738,"line":756},[1736,52607,52608],{"class":5036},"  myFontSize",[1736,52610,8830],{"class":4866},[1736,52612,6866],{"class":1918},[1736,52614,52615,52618,52620],{"class":1738,"line":1755},[1736,52616,52617],{"class":5036},"  myBorderColor",[1736,52619,8830],{"class":4866},[1736,52621,6866],{"class":1918},[1736,52623,52624,52627,52629],{"class":1738,"line":1761},[1736,52625,52626],{"class":5036},"  myBorderRadius",[1736,52628,8830],{"class":4866},[1736,52630,6866],{"class":1918},[1736,52632,52633],{"class":1738,"line":1767},[1736,52634,1976],{"class":1912},[11,52636,52637],{},"Add a '?' to make the tokens optional so you can use tokens from the default theme as well as override any tokens the theme wants to change.",[2260,52639,52640],{"start":756},[73,52641,52574,52642,52645],{},[179,52643,52644],{},"default-theme-tokens.tsx"," file which should include all design tokens and values for your default theme.",[299,52647,52651],{"className":52648,"code":52649,"language":52650,"meta":307,"style":307},"language-Js shiki shiki-themes github-light github-dark","import { ThemeSchema } from './theme-schema';\n\nexport const defaultTheme: ThemeSchema = {\n  myColorBackground: '#ffffff',\n  myColorText: 'purple',\n  myFontSize: '26px',\n  myBorderColor: 'purple',\n  myBorderRadius: '5px',\n};\n","Js",[179,52652,52653,52658,52662,52667,52672,52677,52682,52687,52692],{"__ignoreMap":307},[1736,52654,52655],{"class":1738,"line":1739},[1736,52656,52657],{},"import { ThemeSchema } from './theme-schema';\n",[1736,52659,52660],{"class":1738,"line":748},[1736,52661,1747],{"emptyLinePlaceholder":790},[1736,52663,52664],{"class":1738,"line":756},[1736,52665,52666],{},"export const defaultTheme: ThemeSchema = {\n",[1736,52668,52669],{"class":1738,"line":1755},[1736,52670,52671],{},"  myColorBackground: '#ffffff',\n",[1736,52673,52674],{"class":1738,"line":1761},[1736,52675,52676],{},"  myColorText: 'purple',\n",[1736,52678,52679],{"class":1738,"line":1767},[1736,52680,52681],{},"  myFontSize: '26px',\n",[1736,52683,52684],{"class":1738,"line":1772},[1736,52685,52686],{},"  myBorderColor: 'purple',\n",[1736,52688,52689],{"class":1738,"line":1778},[1736,52690,52691],{},"  myBorderRadius: '5px',\n",[1736,52693,52694],{"class":1738,"line":1784},[1736,52695,15179],{},[2260,52697,52698],{"start":1755},[73,52699,52700,52701,52704],{},"Install the ",[179,52702,52703],{},"ThemeProvider"," component from teambit.",[299,52706,52708],{"className":2665,"code":52707,"language":2667,"meta":307,"style":307},"bit install @teambit/base-react.theme.theme-provider\n// or\nnpm install @teambit/base-react.theme.theme-provider\n",[179,52709,52710,52719,52727],{"__ignoreMap":307},[1736,52711,52712,52714,52716],{"class":1738,"line":1739},[1736,52713,4970],{"class":2674},[1736,52715,4973],{"class":1935},[1736,52717,52718],{"class":1935}," @teambit/base-react.theme.theme-provider\n",[1736,52720,52721,52724],{"class":1738,"line":748},[1736,52722,52723],{"class":2674},"//",[1736,52725,52726],{"class":1935}," or\n",[1736,52728,52729,52731,52733],{"class":1738,"line":756},[1736,52730,6565],{"class":2674},[1736,52732,4973],{"class":1935},[1736,52734,52718],{"class":1935},[2260,52736,52737],{"start":1761},[73,52738,52739,52740,52743,52744,52747,52748,52751,52752,52755,52756,52759,52760,52762],{},"Import the ",[179,52741,52742],{},"createTheme"," function from teambit's Theme Provider component and the ",[179,52745,52746],{},"defaultTheme"," from the ",[179,52749,52750],{},"default-theme-tokens"," file in your ",[179,52753,52754],{},"theme-provider.tsx"," file. Create your theme using the ",[179,52757,52758],{},"createTheme()"," function passing into it the ",[179,52761,52746],{}," as the value for theme.",[299,52764,52766],{"className":8734,"code":52765,"language":8736,"meta":307,"style":307},"import { createTheme } from '@teambit/base-react.theme.theme-provider'\nimport { defaultTheme } from './default-theme-tokens'\n\nexport const Theme = createTheme({\n  theme: defaultTheme\n})\n",[179,52767,52768,52778,52790,52794,52810,52815],{"__ignoreMap":307},[1736,52769,52770,52772,52774,52776],{"class":1738,"line":1739},[1736,52771,4996],{"class":4866},[1736,52773,23272],{"class":1912},[1736,52775,5002],{"class":4866},[1736,52777,23277],{"class":1935},[1736,52779,52780,52782,52785,52787],{"class":1738,"line":748},[1736,52781,4996],{"class":4866},[1736,52783,52784],{"class":1912}," { defaultTheme } ",[1736,52786,5002],{"class":4866},[1736,52788,52789],{"class":1935}," './default-theme-tokens'\n",[1736,52791,52792],{"class":1738,"line":756},[1736,52793,1747],{"emptyLinePlaceholder":790},[1736,52795,52796,52798,52800,52803,52805,52808],{"class":1738,"line":1755},[1736,52797,6632],{"class":4866},[1736,52799,7002],{"class":4866},[1736,52801,52802],{"class":1918}," Theme",[1736,52804,4911],{"class":4866},[1736,52806,52807],{"class":2674}," createTheme",[1736,52809,5122],{"class":1912},[1736,52811,52812],{"class":1738,"line":1761},[1736,52813,52814],{"class":1912},"  theme: defaultTheme\n",[1736,52816,52817],{"class":1738,"line":1767},[1736,52818,10582],{"class":1912},[11,52820,52821],{},"You supply the design tokens directly to the createTheme function and it does the rest for you to convert the tokens to usable css (both in modules and directly in your css).",[2260,52823,52824],{"start":1767},[73,52825,52826,52827,829,52832,608,52837,891],{},"Add ",[15,52828,52831],{"href":52829,"rel":52830},"https://bit.dev/learn-bit-react/theming/themes/theme-provider/~code/theme-provider.composition.tsx",[19],"compositions",[15,52833,52836],{"href":52834,"rel":52835},"https://bit.dev/learn-bit-react/theming/themes/theme-provider/~code/theme-provider.spec.tsx",[19],"tests",[15,52838,41523],{"href":52839,"rel":52840},"https://bit.dev/learn-bit-react/theming/themes/theme-provider/~code/theme-provider.docs.mdx",[19],[23,52842,52844],{"id":52843},"step-2-use-your-theme-provider","Step 2: Use your Theme Provider",[2260,52846,52847],{},[73,52848,52849],{},"Import the Theme from the ThemeProvider component you created.",[299,52851,52853],{"className":8734,"code":52852,"language":8736,"meta":307,"style":307},"import { Theme } from '@learn-bit-react/theming.themes.theme-provider'\n",[179,52854,52855],{"__ignoreMap":307},[1736,52856,52857,52859,52862,52864],{"class":1738,"line":1739},[1736,52858,4996],{"class":4866},[1736,52860,52861],{"class":1912}," { Theme } ",[1736,52863,5002],{"class":4866},[1736,52865,52866],{"class":1935}," '@learn-bit-react/theming.themes.theme-provider'\n",[2260,52868,52869],{"start":748},[73,52870,52871],{},"Wrap all components in the theme provider to see the theme applied to your components.",[299,52873,52875],{"className":8734,"code":52874,"language":8736,"meta":307,"style":307},"import React from 'react'\nimport { Theme } from '@learn-bit-react/theming.themes.theme-provider'\nimport { Button } from '@learn-bit-react/theming.example.button'\n\nexport const MyApp = () => {\n  return (\n    \u003CTheme.ThemeProvider>\n      \u003CButton>Hello\u003C/Button>\n    \u003C/Theme.ThemeProvider>\n  )\n}\n",[179,52876,52877,52887,52897,52908,52912,52929,52935,52944,52957,52965,52969],{"__ignoreMap":307},[1736,52878,52879,52881,52883,52885],{"class":1738,"line":1739},[1736,52880,4996],{"class":4866},[1736,52882,6594],{"class":1912},[1736,52884,5002],{"class":4866},[1736,52886,6599],{"class":1935},[1736,52888,52889,52891,52893,52895],{"class":1738,"line":748},[1736,52890,4996],{"class":4866},[1736,52892,52861],{"class":1912},[1736,52894,5002],{"class":4866},[1736,52896,52866],{"class":1935},[1736,52898,52899,52901,52903,52905],{"class":1738,"line":756},[1736,52900,4996],{"class":4866},[1736,52902,8756],{"class":1912},[1736,52904,5002],{"class":4866},[1736,52906,52907],{"class":1935}," '@learn-bit-react/theming.example.button'\n",[1736,52909,52910],{"class":1738,"line":1755},[1736,52911,1747],{"emptyLinePlaceholder":790},[1736,52913,52914,52916,52918,52921,52923,52925,52927],{"class":1738,"line":1761},[1736,52915,6632],{"class":4866},[1736,52917,7002],{"class":4866},[1736,52919,52920],{"class":2674}," MyApp",[1736,52922,4911],{"class":4866},[1736,52924,7010],{"class":1912},[1736,52926,7013],{"class":4866},[1736,52928,4914],{"class":1912},[1736,52930,52931,52933],{"class":1738,"line":1767},[1736,52932,6685],{"class":4866},[1736,52934,6688],{"class":1912},[1736,52936,52937,52939,52942],{"class":1738,"line":1772},[1736,52938,6693],{"class":1912},[1736,52940,52941],{"class":1918},"Theme.ThemeProvider",[1736,52943,6663],{"class":1912},[1736,52945,52946,52948,52950,52953,52955],{"class":1738,"line":1778},[1736,52947,6710],{"class":1912},[1736,52949,9089],{"class":1918},[1736,52951,52952],{"class":1912},">Hello\u003C/",[1736,52954,9089],{"class":1918},[1736,52956,6663],{"class":1912},[1736,52958,52959,52961,52963],{"class":1738,"line":1784},[1736,52960,6744],{"class":1912},[1736,52962,52941],{"class":1918},[1736,52964,6663],{"class":1912},[1736,52966,52967],{"class":1738,"line":1790},[1736,52968,6753],{"class":1912},[1736,52970,52971],{"class":1738,"line":1796},[1736,52972,1976],{"class":1912},[138,52974,52976],{"id":52975},"button-using-default-theme","Button using Default Theme",[11,52978,52979],{},"You can install and use the Button component from this demo as an example or create your own and add some styles to it using css variables.",[23,52981,52983],{"id":52982},"step-3-create-a-dark-theme-component","Step 3: Create a Dark Theme Component",[2260,52985,52986],{},[73,52987,52988],{},"Create a new theme component.",[299,52990,52992],{"className":2665,"code":52991,"language":2667,"meta":307,"style":307},"bit create react themes/dark-theme\n",[179,52993,52994],{"__ignoreMap":307},[1736,52995,52996,52998,53000,53003],{"class":1738,"line":1739},[1736,52997,4970],{"class":2674},[1736,52999,44167],{"class":1935},[1736,53001,53002],{"class":1935}," react",[1736,53004,53005],{"class":1935}," themes/dark-theme\n",[2260,53007,53008],{"start":748},[73,53009,52574,53010,53013],{},[179,53011,53012],{},"dark-theme-tokens.tsx"," file with the design tokens and values you want to override from the default theme.",[299,53015,53017],{"className":8734,"code":53016,"language":8736,"meta":307,"style":307},"import type { ThemeSchema } from '@learn-bit-react/theming.themes.theme-provider'\n\nexport const darkTheme: ThemeSchema = {\n  myColorBackground: '#000',\n  myColorText: 'red',\n  myBorderColor: 'red'\n}\n",[179,53018,53019,53032,53036,53053,53063,53073,53080],{"__ignoreMap":307},[1736,53020,53021,53023,53025,53028,53030],{"class":1738,"line":1739},[1736,53022,4996],{"class":4866},[1736,53024,6635],{"class":4866},[1736,53026,53027],{"class":1912}," { ThemeSchema } ",[1736,53029,5002],{"class":4866},[1736,53031,52866],{"class":1935},[1736,53033,53034],{"class":1738,"line":748},[1736,53035,1747],{"emptyLinePlaceholder":790},[1736,53037,53038,53040,53042,53045,53047,53049,53051],{"class":1738,"line":756},[1736,53039,6632],{"class":4866},[1736,53041,7002],{"class":4866},[1736,53043,53044],{"class":1918}," darkTheme",[1736,53046,1087],{"class":4866},[1736,53048,52592],{"class":2674},[1736,53050,4911],{"class":4866},[1736,53052,4914],{"class":1912},[1736,53054,53055,53058,53061],{"class":1738,"line":1755},[1736,53056,53057],{"class":1912},"  myColorBackground: ",[1736,53059,53060],{"class":1935},"'#000'",[1736,53062,1939],{"class":1912},[1736,53064,53065,53068,53071],{"class":1738,"line":1761},[1736,53066,53067],{"class":1912},"  myColorText: ",[1736,53069,53070],{"class":1935},"'red'",[1736,53072,1939],{"class":1912},[1736,53074,53075,53078],{"class":1738,"line":1767},[1736,53076,53077],{"class":1912},"  myBorderColor: ",[1736,53079,41508],{"class":1935},[1736,53081,53082],{"class":1738,"line":1772},[1736,53083,1976],{"class":1912},[11,53085,53086],{},"Import the ThemeSchema for type checking.",[2260,53088,53089],{"start":756},[73,53090,52739,53091,53094,53095,52747,53098,2900],{},[179,53092,53093],{},"Theme"," from the the theme provider component you just created as well as the ",[179,53096,53097],{},"darkTheme",[179,53099,53100],{},"dark-theme-tokens",[299,53102,53104],{"className":52648,"code":53103,"language":52650,"meta":307,"style":307},"import React from 'react';\nimport { Theme } from '@learn-bit-react/theming.themes.theme-provider';\nimport { darkTheme } from './dark-theme-tokens';\n\nexport const DarkTheme = ({ children }) => {\n  return (\n    \u003CTheme.ThemeProvider overrides={darkTheme}>\n      {children}\n    \u003C/Theme.ThemeProvider>\n  );\n};\n",[179,53105,53106,53111,53116,53121,53125,53130,53135,53140,53145,53150,53154],{"__ignoreMap":307},[1736,53107,53108],{"class":1738,"line":1739},[1736,53109,53110],{},"import React from 'react';\n",[1736,53112,53113],{"class":1738,"line":748},[1736,53114,53115],{},"import { Theme } from '@learn-bit-react/theming.themes.theme-provider';\n",[1736,53117,53118],{"class":1738,"line":756},[1736,53119,53120],{},"import { darkTheme } from './dark-theme-tokens';\n",[1736,53122,53123],{"class":1738,"line":1755},[1736,53124,1747],{"emptyLinePlaceholder":790},[1736,53126,53127],{"class":1738,"line":1761},[1736,53128,53129],{},"export const DarkTheme = ({ children }) => {\n",[1736,53131,53132],{"class":1738,"line":1767},[1736,53133,53134],{},"  return (\n",[1736,53136,53137],{"class":1738,"line":1772},[1736,53138,53139],{},"    \u003CTheme.ThemeProvider overrides={darkTheme}>\n",[1736,53141,53142],{"class":1738,"line":1778},[1736,53143,53144],{},"      {children}\n",[1736,53146,53147],{"class":1738,"line":1784},[1736,53148,53149],{},"    \u003C/Theme.ThemeProvider>\n",[1736,53151,53152],{"class":1738,"line":1790},[1736,53153,7956],{},[1736,53155,53156],{"class":1738,"line":1796},[1736,53157,15179],{},[11,53159,53160],{},"Add the darkTheme to the overrides prop of the theme provider.",[2260,53162,53163],{"start":1755},[73,53164,53165,53166,53169,53170,53172],{},"Make sure your index file is exporting both the ",[179,53167,53168],{},"DarkTheme"," component as well as the ",[179,53171,53097],{}," tokens.",[299,53174,53176],{"className":52648,"code":53175,"language":52650,"meta":307,"style":307},"export { DarkTheme } from './dark-theme';\nexport { darkTheme } from './dark-theme-tokens';\n",[179,53177,53178,53183],{"__ignoreMap":307},[1736,53179,53180],{"class":1738,"line":1739},[1736,53181,53182],{},"export { DarkTheme } from './dark-theme';\n",[1736,53184,53185],{"class":1738,"line":748},[1736,53186,53187],{},"export { darkTheme } from './dark-theme-tokens';\n",[2260,53189,53190],{"start":1761},[73,53191,52826,53192,829,53196,608,53200,891],{},[15,53193,52831],{"href":53194,"rel":53195},"https://bit.dev/learn-bit-react/theming/themes/dark-theme/~code/dark-theme.composition.tsx",[19],[15,53197,52836],{"href":53198,"rel":53199},"https://bit.dev/learn-bit-react/theming/themes/dark-theme/~code/dark-theme.spec.tsx",[19],[15,53201,41523],{"href":53202,"rel":53203},"https://bit.dev/learn-bit-react/theming/themes/dark-theme/~code/dark-theme.docs.mdx",[19],[23,53205,53207],{"id":53206},"step-4-use-your-theme-in-a-component","Step 4: Use your Theme in a Component",[2260,53209,53210],{},[73,53211,53212],{},"Import the Theme from the ThemeProvider component you created as well as the DarkTheme and any components you want to render.",[299,53214,53216],{"className":8734,"code":53215,"language":8736,"meta":307,"style":307},"import React from 'react'\nimport { Theme } from '@learn-bit-react/theming.themes.theme-provider'\nimport { DarkTheme } from '@learn-bit-react/theming.themes.dark-theme'\nimport { Button } from '@learn-bit-react/theming.example.button'\n",[179,53217,53218,53228,53238,53250],{"__ignoreMap":307},[1736,53219,53220,53222,53224,53226],{"class":1738,"line":1739},[1736,53221,4996],{"class":4866},[1736,53223,6594],{"class":1912},[1736,53225,5002],{"class":4866},[1736,53227,6599],{"class":1935},[1736,53229,53230,53232,53234,53236],{"class":1738,"line":748},[1736,53231,4996],{"class":4866},[1736,53233,52861],{"class":1912},[1736,53235,5002],{"class":4866},[1736,53237,52866],{"class":1935},[1736,53239,53240,53242,53245,53247],{"class":1738,"line":756},[1736,53241,4996],{"class":4866},[1736,53243,53244],{"class":1912}," { DarkTheme } ",[1736,53246,5002],{"class":4866},[1736,53248,53249],{"class":1935}," '@learn-bit-react/theming.themes.dark-theme'\n",[1736,53251,53252,53254,53256,53258],{"class":1738,"line":1755},[1736,53253,4996],{"class":4866},[1736,53255,8756],{"class":1912},[1736,53257,5002],{"class":4866},[1736,53259,52907],{"class":1935},[11,53261,53262],{},"Wrap all components in the Theme Provider component and use the overrides prop to change the theme to darkTheme.",[299,53264,53266],{"className":8734,"code":53265,"language":8736,"meta":307,"style":307},"export const MyApp = () => {\n  return (\n    \u003CTheme.ThemeProvider overrides={darkTheme}>\n      \u003CButton>Hello\u003C/Button>\n    \u003C/Theme.ThemeProvider>\n  )\n}\n",[179,53267,53268,53284,53290,53303,53315,53323,53327],{"__ignoreMap":307},[1736,53269,53270,53272,53274,53276,53278,53280,53282],{"class":1738,"line":1739},[1736,53271,6632],{"class":4866},[1736,53273,7002],{"class":4866},[1736,53275,52920],{"class":2674},[1736,53277,4911],{"class":4866},[1736,53279,7010],{"class":1912},[1736,53281,7013],{"class":4866},[1736,53283,4914],{"class":1912},[1736,53285,53286,53288],{"class":1738,"line":748},[1736,53287,6685],{"class":4866},[1736,53289,6688],{"class":1912},[1736,53291,53292,53294,53296,53298,53300],{"class":1738,"line":756},[1736,53293,6693],{"class":1912},[1736,53295,52941],{"class":1918},[1736,53297,23300],{"class":2674},[1736,53299,5062],{"class":4866},[1736,53301,53302],{"class":1912},"{darkTheme}>\n",[1736,53304,53305,53307,53309,53311,53313],{"class":1738,"line":1755},[1736,53306,6710],{"class":1912},[1736,53308,9089],{"class":1918},[1736,53310,52952],{"class":1912},[1736,53312,9089],{"class":1918},[1736,53314,6663],{"class":1912},[1736,53316,53317,53319,53321],{"class":1738,"line":1761},[1736,53318,6744],{"class":1912},[1736,53320,52941],{"class":1918},[1736,53322,6663],{"class":1912},[1736,53324,53325],{"class":1738,"line":1767},[1736,53326,6753],{"class":1912},[1736,53328,53329],{"class":1738,"line":1772},[1736,53330,1976],{"class":1912},[138,53332,53334],{"id":53333},"button-with-dark-theme-applied","Button with Dark Theme applied",[11,53336,53337],{},"Use theme like a wrapper component You can alternatively use the component like this which will work exactly the same as the example above.",[299,53339,53341],{"className":8734,"code":53340,"language":8736,"meta":307,"style":307},"export const MyApp = () => {\n  return (\n    \u003CDarkTheme>\n      \u003CButton>Dark Theme\u003C/Button>\n    \u003C/DarkTheme>\n  )\n}\n",[179,53342,53343,53359,53365,53373,53386,53394,53398],{"__ignoreMap":307},[1736,53344,53345,53347,53349,53351,53353,53355,53357],{"class":1738,"line":1739},[1736,53346,6632],{"class":4866},[1736,53348,7002],{"class":4866},[1736,53350,52920],{"class":2674},[1736,53352,4911],{"class":4866},[1736,53354,7010],{"class":1912},[1736,53356,7013],{"class":4866},[1736,53358,4914],{"class":1912},[1736,53360,53361,53363],{"class":1738,"line":748},[1736,53362,6685],{"class":4866},[1736,53364,6688],{"class":1912},[1736,53366,53367,53369,53371],{"class":1738,"line":756},[1736,53368,6693],{"class":1912},[1736,53370,53168],{"class":1918},[1736,53372,6663],{"class":1912},[1736,53374,53375,53377,53379,53382,53384],{"class":1738,"line":1755},[1736,53376,6710],{"class":1912},[1736,53378,9089],{"class":1918},[1736,53380,53381],{"class":1912},">Dark Theme\u003C/",[1736,53383,9089],{"class":1918},[1736,53385,6663],{"class":1912},[1736,53387,53388,53390,53392],{"class":1738,"line":1761},[1736,53389,6744],{"class":1912},[1736,53391,53168],{"class":1918},[1736,53393,6663],{"class":1912},[1736,53395,53396],{"class":1738,"line":1767},[1736,53397,6753],{"class":1912},[1736,53399,53400],{"class":1738,"line":1772},[1736,53401,1976],{"class":1912},[138,53403,53405],{"id":53404},"multiple-themes","Multiple Themes",[11,53407,53408],{},"You can also use the default theme and have a part of your app be wrapped in a different theme.",[299,53410,53412],{"className":8734,"code":53411,"language":8736,"meta":307,"style":307},"export const MyApp = () => {\n  return (\n    \u003CTheme.ThemeProvider>\n      \u003CButton>Default Theme\u003C/Button>\n      {/* A dark themed button inside a default theme */}\n      \u003CDarkTheme>\n        \u003CButton>Dark Theme\u003C/Button>\n      \u003C/DarkTheme>\n    \u003C/Theme.ThemeProvider>\n  )\n}\n",[179,53413,53414,53430,53436,53444,53457,53466,53474,53486,53494,53502,53506],{"__ignoreMap":307},[1736,53415,53416,53418,53420,53422,53424,53426,53428],{"class":1738,"line":1739},[1736,53417,6632],{"class":4866},[1736,53419,7002],{"class":4866},[1736,53421,52920],{"class":2674},[1736,53423,4911],{"class":4866},[1736,53425,7010],{"class":1912},[1736,53427,7013],{"class":4866},[1736,53429,4914],{"class":1912},[1736,53431,53432,53434],{"class":1738,"line":748},[1736,53433,6685],{"class":4866},[1736,53435,6688],{"class":1912},[1736,53437,53438,53440,53442],{"class":1738,"line":756},[1736,53439,6693],{"class":1912},[1736,53441,52941],{"class":1918},[1736,53443,6663],{"class":1912},[1736,53445,53446,53448,53450,53453,53455],{"class":1738,"line":1755},[1736,53447,6710],{"class":1912},[1736,53449,9089],{"class":1918},[1736,53451,53452],{"class":1912},">Default Theme\u003C/",[1736,53454,9089],{"class":1918},[1736,53456,6663],{"class":1912},[1736,53458,53459,53461,53464],{"class":1738,"line":1761},[1736,53460,11962],{"class":1912},[1736,53462,53463],{"class":6820},"/* A dark themed button inside a default theme */",[1736,53465,1976],{"class":1912},[1736,53467,53468,53470,53472],{"class":1738,"line":1767},[1736,53469,6710],{"class":1912},[1736,53471,53168],{"class":1918},[1736,53473,6663],{"class":1912},[1736,53475,53476,53478,53480,53482,53484],{"class":1738,"line":1772},[1736,53477,8246],{"class":1912},[1736,53479,9089],{"class":1918},[1736,53481,53381],{"class":1912},[1736,53483,9089],{"class":1918},[1736,53485,6663],{"class":1912},[1736,53487,53488,53490,53492],{"class":1738,"line":1778},[1736,53489,8261],{"class":1912},[1736,53491,53168],{"class":1918},[1736,53493,6663],{"class":1912},[1736,53495,53496,53498,53500],{"class":1738,"line":1784},[1736,53497,6744],{"class":1912},[1736,53499,52941],{"class":1918},[1736,53501,6663],{"class":1912},[1736,53503,53504],{"class":1738,"line":1790},[1736,53505,6753],{"class":1912},[1736,53507,53508],{"class":1738,"line":1796},[1736,53509,1976],{"class":1912},[23,53511,3294],{"id":3293},[11,53513,53514],{},"And thats how you do theming in React. For more info on how the components work or to learn more on theming and React context check out:",[70,53516,53517,53524,53531,53538,53545],{},[73,53518,53519],{},[15,53520,53523],{"href":53521,"rel":53522},"https://bit.dev/learn-bit-react/theming/readme",[19],"Theming in Bit",[73,53525,53526],{},[15,53527,53530],{"href":53528,"rel":53529},"https://bit.dev/learn-bit-react/theming/example/button",[19],"Example Button Component",[73,53532,53533],{},[15,53534,53537],{"href":53535,"rel":53536},"https://bit.dev/learn-bit-react/theming/themes/theme-provider",[19],"Theme Provider Component",[73,53539,53540,891],{},[15,53541,53544],{"href":53542,"rel":53543},"https://bit.dev/learn-bit-react/theming/themes/dark-theme",[19],"Dark Theme",[73,53546,53547],{},[15,53548,53551],{"href":53549,"rel":53550},"https://beta.reactjs.org/apis/usecontext",[19],"React Docs Theming",[2011,53553,53554],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":307,"searchDepth":748,"depth":748,"links":53556},[53557,53558,53561,53562,53566],{"id":52565,"depth":748,"text":52566},{"id":52843,"depth":748,"text":52844,"children":53559},[53560],{"id":52975,"depth":756,"text":52976},{"id":52982,"depth":748,"text":52983},{"id":53206,"depth":748,"text":53207,"children":53563},[53564,53565],{"id":53333,"depth":756,"text":53334},{"id":53404,"depth":756,"text":53405},{"id":3293,"depth":748,"text":3294},"2022-03-10","photo-1595446472721-4b7aa63a2bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=80",{"ogImage":53570},"https://images.unsplash.com/photo-1595446472721-4b7aa63a2bc7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=80?fm=webp&fit=crop&q=80&w=480","/blog/theming-in-react",{"title":52539,"description":52544},"blog/theming-in-react",[5221],"g1QCYg-yHH5kmSaGIajneZVeDatN05Oej2eRE0BtVV4",{"id":53577,"title":53578,"body":53579,"canonical":788,"date":54642,"description":53583,"extension":786,"featured":787,"image":54643,"meta":54644,"navigation":790,"ogimage":788,"path":54645,"provider":5235,"published":787,"seo":54646,"stem":54647,"tags":54648,"url":788,"__hash__":54649},"blog/blog/typescript-and-children.md","TypeScript and Children",{"type":8,"value":53580,"toc":54631},[53581,53584,53587,53590,53594,53597,53719,53724,53727,53862,53868,53871,54063,54067,54070,54166,54171,54174,54298,54304,54307,54460,54464,54467,54591,54593,54596,54598,54628],[11,53582,53583],{},"What happens when we pass in children in React? Children is a special prop that allows us to pass in any type of element. It could be a number, a string, a boolean, an array of elements or even another component. So how can we check the types?",[11,53585,53586],{},"Of course we could define it as any which is basically the same as just not having type checking which defeats the whole purpose of using Typescript.",[11,53588,53589],{},"There are a couple of types we could choose from:",[138,53591,53593],{"id":53592},"jsxelement","JSX.Element",[11,53595,53596],{},"Children must be a single JSX element. Doesn't allow multiple children, or strings etc.",[299,53598,53600],{"className":8734,"code":53599,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: JSX.Element\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport function Card() {\n  return (\n    \u003CButton>\n      \u003Cspan>Click me\u003C/span>\n    \u003C/Button>\n  )\n}\n",[179,53601,53602,53612,53626,53630,53662,53666,53676,53682,53690,53703,53711,53715],{"__ignoreMap":307},[1736,53603,53604,53607,53610],{"class":1738,"line":1739},[1736,53605,53606],{"class":4866},"interface",[1736,53608,53609],{"class":2674}," ButtonProps",[1736,53611,4914],{"class":1912},[1736,53613,53614,53616,53618,53621,53623],{"class":1738,"line":748},[1736,53615,14140],{"class":5036},[1736,53617,1087],{"class":4866},[1736,53619,53620],{"class":2674}," JSX",[1736,53622,891],{"class":1912},[1736,53624,53625],{"class":2674},"Element\n",[1736,53627,53628],{"class":1738,"line":756},[1736,53629,1976],{"class":1912},[1736,53631,53632,53634,53636,53638,53640,53642,53644,53646,53648,53650,53652,53654,53656,53658,53660],{"class":1738,"line":1755},[1736,53633,5029],{"class":4866},[1736,53635,30443],{"class":2674},[1736,53637,4911],{"class":4866},[1736,53639,17735],{"class":1912},[1736,53641,31760],{"class":5036},[1736,53643,7239],{"class":1912},[1736,53645,1087],{"class":4866},[1736,53647,53609],{"class":2674},[1736,53649,8939],{"class":1912},[1736,53651,7013],{"class":4866},[1736,53653,10193],{"class":1912},[1736,53655,14849],{"class":6696},[1736,53657,31779],{"class":1912},[1736,53659,14849],{"class":6696},[1736,53661,6663],{"class":1912},[1736,53663,53664],{"class":1738,"line":1761},[1736,53665,1747],{"emptyLinePlaceholder":790},[1736,53667,53668,53670,53672,53674],{"class":1738,"line":1767},[1736,53669,6632],{"class":4866},[1736,53671,6674],{"class":4866},[1736,53673,31559],{"class":2674},[1736,53675,6680],{"class":1912},[1736,53677,53678,53680],{"class":1738,"line":1772},[1736,53679,6685],{"class":4866},[1736,53681,6688],{"class":1912},[1736,53683,53684,53686,53688],{"class":1738,"line":1778},[1736,53685,6693],{"class":1912},[1736,53687,9089],{"class":1918},[1736,53689,6663],{"class":1912},[1736,53691,53692,53694,53696,53699,53701],{"class":1738,"line":1784},[1736,53693,6710],{"class":1912},[1736,53695,1736],{"class":6696},[1736,53697,53698],{"class":1912},">Click me\u003C/",[1736,53700,1736],{"class":6696},[1736,53702,6663],{"class":1912},[1736,53704,53705,53707,53709],{"class":1738,"line":1790},[1736,53706,6744],{"class":1912},[1736,53708,9089],{"class":1918},[1736,53710,6663],{"class":1912},[1736,53712,53713],{"class":1738,"line":1796},[1736,53714,6753],{"class":1912},[1736,53716,53717],{"class":1738,"line":2353},[1736,53718,1976],{"class":1912},[138,53720,53593,53722],{"id":53721},"jsxelement-1",[1736,53723],{},[11,53725,53726],{},"Allows multiple JSX elements but no strings, numbers etc",[299,53728,53730],{"className":8734,"code":53729,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: JSX.Element[]\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport default function Card() {\n  return (\n    \u003CButton>\n      \u003Cspan>click me\u003C/span>\n      \u003Ci>svg icon\u003C/i>\n    \u003C/Button>\n  )\n}\n",[179,53731,53732,53740,53755,53759,53791,53795,53807,53813,53821,53833,53846,53854,53858],{"__ignoreMap":307},[1736,53733,53734,53736,53738],{"class":1738,"line":1739},[1736,53735,53606],{"class":4866},[1736,53737,53609],{"class":2674},[1736,53739,4914],{"class":1912},[1736,53741,53742,53744,53746,53748,53750,53753],{"class":1738,"line":748},[1736,53743,14140],{"class":5036},[1736,53745,1087],{"class":4866},[1736,53747,53620],{"class":2674},[1736,53749,891],{"class":1912},[1736,53751,53752],{"class":2674},"Element",[1736,53754,35984],{"class":1912},[1736,53756,53757],{"class":1738,"line":756},[1736,53758,1976],{"class":1912},[1736,53760,53761,53763,53765,53767,53769,53771,53773,53775,53777,53779,53781,53783,53785,53787,53789],{"class":1738,"line":1755},[1736,53762,5029],{"class":4866},[1736,53764,30443],{"class":2674},[1736,53766,4911],{"class":4866},[1736,53768,17735],{"class":1912},[1736,53770,31760],{"class":5036},[1736,53772,7239],{"class":1912},[1736,53774,1087],{"class":4866},[1736,53776,53609],{"class":2674},[1736,53778,8939],{"class":1912},[1736,53780,7013],{"class":4866},[1736,53782,10193],{"class":1912},[1736,53784,14849],{"class":6696},[1736,53786,31779],{"class":1912},[1736,53788,14849],{"class":6696},[1736,53790,6663],{"class":1912},[1736,53792,53793],{"class":1738,"line":1761},[1736,53794,1747],{"emptyLinePlaceholder":790},[1736,53796,53797,53799,53801,53803,53805],{"class":1738,"line":1767},[1736,53798,6632],{"class":4866},[1736,53800,30438],{"class":4866},[1736,53802,6674],{"class":4866},[1736,53804,31559],{"class":2674},[1736,53806,6680],{"class":1912},[1736,53808,53809,53811],{"class":1738,"line":1772},[1736,53810,6685],{"class":4866},[1736,53812,6688],{"class":1912},[1736,53814,53815,53817,53819],{"class":1738,"line":1778},[1736,53816,6693],{"class":1912},[1736,53818,9089],{"class":1918},[1736,53820,6663],{"class":1912},[1736,53822,53823,53825,53827,53829,53831],{"class":1738,"line":1784},[1736,53824,6710],{"class":1912},[1736,53826,1736],{"class":6696},[1736,53828,30456],{"class":1912},[1736,53830,1736],{"class":6696},[1736,53832,6663],{"class":1912},[1736,53834,53835,53837,53839,53842,53844],{"class":1738,"line":1790},[1736,53836,6710],{"class":1912},[1736,53838,13031],{"class":6696},[1736,53840,53841],{"class":1912},">svg icon\u003C/",[1736,53843,13031],{"class":6696},[1736,53845,6663],{"class":1912},[1736,53847,53848,53850,53852],{"class":1738,"line":1796},[1736,53849,6744],{"class":1912},[1736,53851,9089],{"class":1918},[1736,53853,6663],{"class":1912},[1736,53855,53856],{"class":1738,"line":2353},[1736,53857,6753],{"class":1912},[1736,53859,53860],{"class":1738,"line":2358},[1736,53861,1976],{"class":1912},[138,53863,53865,53866],{"id":53864},"jsxelement-jsxelement","JSX.Element | JSX.Element",[1736,53867],{},[11,53869,53870],{},"Allows single or multiple JSX elements but no strings, numbers etc",[299,53872,53874],{"className":8734,"code":53873,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: JSX.Element | JSX.Element[]\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport function Card() {\n  return (\n    \u003CButton>\n      \u003Cspan>click me\u003C/span>\n    \u003C/Button>\n  )\n}\nexport function Card2() {\n  return (\n    \u003CButton>\n      \u003Cspan>click me\u003C/span>\n      \u003Ci>svg icon\u003C/i>\n    \u003C/Button>\n  )\n}\n",[179,53875,53876,53884,53906,53910,53942,53946,53956,53962,53970,53982,53990,53994,53998,54009,54015,54023,54035,54047,54055,54059],{"__ignoreMap":307},[1736,53877,53878,53880,53882],{"class":1738,"line":1739},[1736,53879,53606],{"class":4866},[1736,53881,53609],{"class":2674},[1736,53883,4914],{"class":1912},[1736,53885,53886,53888,53890,53892,53894,53896,53898,53900,53902,53904],{"class":1738,"line":748},[1736,53887,14140],{"class":5036},[1736,53889,1087],{"class":4866},[1736,53891,53620],{"class":2674},[1736,53893,891],{"class":1912},[1736,53895,53752],{"class":2674},[1736,53897,35748],{"class":4866},[1736,53899,53620],{"class":2674},[1736,53901,891],{"class":1912},[1736,53903,53752],{"class":2674},[1736,53905,35984],{"class":1912},[1736,53907,53908],{"class":1738,"line":756},[1736,53909,1976],{"class":1912},[1736,53911,53912,53914,53916,53918,53920,53922,53924,53926,53928,53930,53932,53934,53936,53938,53940],{"class":1738,"line":1755},[1736,53913,5029],{"class":4866},[1736,53915,30443],{"class":2674},[1736,53917,4911],{"class":4866},[1736,53919,17735],{"class":1912},[1736,53921,31760],{"class":5036},[1736,53923,7239],{"class":1912},[1736,53925,1087],{"class":4866},[1736,53927,53609],{"class":2674},[1736,53929,8939],{"class":1912},[1736,53931,7013],{"class":4866},[1736,53933,10193],{"class":1912},[1736,53935,14849],{"class":6696},[1736,53937,31779],{"class":1912},[1736,53939,14849],{"class":6696},[1736,53941,6663],{"class":1912},[1736,53943,53944],{"class":1738,"line":1761},[1736,53945,1747],{"emptyLinePlaceholder":790},[1736,53947,53948,53950,53952,53954],{"class":1738,"line":1767},[1736,53949,6632],{"class":4866},[1736,53951,6674],{"class":4866},[1736,53953,31559],{"class":2674},[1736,53955,6680],{"class":1912},[1736,53957,53958,53960],{"class":1738,"line":1772},[1736,53959,6685],{"class":4866},[1736,53961,6688],{"class":1912},[1736,53963,53964,53966,53968],{"class":1738,"line":1778},[1736,53965,6693],{"class":1912},[1736,53967,9089],{"class":1918},[1736,53969,6663],{"class":1912},[1736,53971,53972,53974,53976,53978,53980],{"class":1738,"line":1784},[1736,53973,6710],{"class":1912},[1736,53975,1736],{"class":6696},[1736,53977,30456],{"class":1912},[1736,53979,1736],{"class":6696},[1736,53981,6663],{"class":1912},[1736,53983,53984,53986,53988],{"class":1738,"line":1790},[1736,53985,6744],{"class":1912},[1736,53987,9089],{"class":1918},[1736,53989,6663],{"class":1912},[1736,53991,53992],{"class":1738,"line":1796},[1736,53993,6753],{"class":1912},[1736,53995,53996],{"class":1738,"line":2353},[1736,53997,1976],{"class":1912},[1736,53999,54000,54002,54004,54007],{"class":1738,"line":2358},[1736,54001,6632],{"class":4866},[1736,54003,6674],{"class":4866},[1736,54005,54006],{"class":2674}," Card2",[1736,54008,6680],{"class":1912},[1736,54010,54011,54013],{"class":1738,"line":2364},[1736,54012,6685],{"class":4866},[1736,54014,6688],{"class":1912},[1736,54016,54017,54019,54021],{"class":1738,"line":2370},[1736,54018,6693],{"class":1912},[1736,54020,9089],{"class":1918},[1736,54022,6663],{"class":1912},[1736,54024,54025,54027,54029,54031,54033],{"class":1738,"line":2376},[1736,54026,6710],{"class":1912},[1736,54028,1736],{"class":6696},[1736,54030,30456],{"class":1912},[1736,54032,1736],{"class":6696},[1736,54034,6663],{"class":1912},[1736,54036,54037,54039,54041,54043,54045],{"class":1738,"line":2381},[1736,54038,6710],{"class":1912},[1736,54040,13031],{"class":6696},[1736,54042,53841],{"class":1912},[1736,54044,13031],{"class":6696},[1736,54046,6663],{"class":1912},[1736,54048,54049,54051,54053],{"class":1738,"line":2387},[1736,54050,6744],{"class":1912},[1736,54052,9089],{"class":1918},[1736,54054,6663],{"class":1912},[1736,54056,54057],{"class":1738,"line":2393},[1736,54058,6753],{"class":1912},[1736,54060,54061],{"class":1738,"line":2398},[1736,54062,1976],{"class":1912},[138,54064,54066],{"id":54065},"reactreactchild","React.ReactChild",[11,54068,54069],{},"Allows one React element, string or number",[299,54071,54073],{"className":8734,"code":54072,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: React.ReactChild\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport default function Card() {\n  return \u003CButton>click me\u003C/Button>\n}\n",[179,54074,54075,54083,54096,54100,54132,54136,54148,54162],{"__ignoreMap":307},[1736,54076,54077,54079,54081],{"class":1738,"line":1739},[1736,54078,53606],{"class":4866},[1736,54080,53609],{"class":2674},[1736,54082,4914],{"class":1912},[1736,54084,54085,54087,54089,54091,54093],{"class":1738,"line":748},[1736,54086,14140],{"class":5036},[1736,54088,1087],{"class":4866},[1736,54090,6649],{"class":2674},[1736,54092,891],{"class":1912},[1736,54094,54095],{"class":2674},"ReactChild\n",[1736,54097,54098],{"class":1738,"line":756},[1736,54099,1976],{"class":1912},[1736,54101,54102,54104,54106,54108,54110,54112,54114,54116,54118,54120,54122,54124,54126,54128,54130],{"class":1738,"line":1755},[1736,54103,5029],{"class":4866},[1736,54105,30443],{"class":2674},[1736,54107,4911],{"class":4866},[1736,54109,17735],{"class":1912},[1736,54111,31760],{"class":5036},[1736,54113,7239],{"class":1912},[1736,54115,1087],{"class":4866},[1736,54117,53609],{"class":2674},[1736,54119,8939],{"class":1912},[1736,54121,7013],{"class":4866},[1736,54123,10193],{"class":1912},[1736,54125,14849],{"class":6696},[1736,54127,31779],{"class":1912},[1736,54129,14849],{"class":6696},[1736,54131,6663],{"class":1912},[1736,54133,54134],{"class":1738,"line":1761},[1736,54135,1747],{"emptyLinePlaceholder":790},[1736,54137,54138,54140,54142,54144,54146],{"class":1738,"line":1767},[1736,54139,6632],{"class":4866},[1736,54141,30438],{"class":4866},[1736,54143,6674],{"class":4866},[1736,54145,31559],{"class":2674},[1736,54147,6680],{"class":1912},[1736,54149,54150,54152,54154,54156,54158,54160],{"class":1738,"line":1772},[1736,54151,6685],{"class":4866},[1736,54153,10193],{"class":1912},[1736,54155,9089],{"class":1918},[1736,54157,30456],{"class":1912},[1736,54159,9089],{"class":1918},[1736,54161,6663],{"class":1912},[1736,54163,54164],{"class":1738,"line":1778},[1736,54165,1976],{"class":1912},[138,54167,54066,54169],{"id":54168},"reactreactchild-1",[1736,54170],{},[11,54172,54173],{},"Allows multiple React elements, strings or numbers",[299,54175,54177],{"className":8734,"code":54176,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: React.ReactChild\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport default function Card() {\n  return (\n    \u003CButton>\n      click me\n      \u003Ci>svg icon\u003C/i>\n    \u003C/Button>\n  )\n}\n",[179,54178,54179,54187,54199,54203,54235,54239,54251,54257,54265,54270,54282,54290,54294],{"__ignoreMap":307},[1736,54180,54181,54183,54185],{"class":1738,"line":1739},[1736,54182,53606],{"class":4866},[1736,54184,53609],{"class":2674},[1736,54186,4914],{"class":1912},[1736,54188,54189,54191,54193,54195,54197],{"class":1738,"line":748},[1736,54190,14140],{"class":5036},[1736,54192,1087],{"class":4866},[1736,54194,6649],{"class":2674},[1736,54196,891],{"class":1912},[1736,54198,54095],{"class":2674},[1736,54200,54201],{"class":1738,"line":756},[1736,54202,1976],{"class":1912},[1736,54204,54205,54207,54209,54211,54213,54215,54217,54219,54221,54223,54225,54227,54229,54231,54233],{"class":1738,"line":1755},[1736,54206,5029],{"class":4866},[1736,54208,30443],{"class":2674},[1736,54210,4911],{"class":4866},[1736,54212,17735],{"class":1912},[1736,54214,31760],{"class":5036},[1736,54216,7239],{"class":1912},[1736,54218,1087],{"class":4866},[1736,54220,53609],{"class":2674},[1736,54222,8939],{"class":1912},[1736,54224,7013],{"class":4866},[1736,54226,10193],{"class":1912},[1736,54228,14849],{"class":6696},[1736,54230,31779],{"class":1912},[1736,54232,14849],{"class":6696},[1736,54234,6663],{"class":1912},[1736,54236,54237],{"class":1738,"line":1761},[1736,54238,1747],{"emptyLinePlaceholder":790},[1736,54240,54241,54243,54245,54247,54249],{"class":1738,"line":1767},[1736,54242,6632],{"class":4866},[1736,54244,30438],{"class":4866},[1736,54246,6674],{"class":4866},[1736,54248,31559],{"class":2674},[1736,54250,6680],{"class":1912},[1736,54252,54253,54255],{"class":1738,"line":1772},[1736,54254,6685],{"class":4866},[1736,54256,6688],{"class":1912},[1736,54258,54259,54261,54263],{"class":1738,"line":1778},[1736,54260,6693],{"class":1912},[1736,54262,9089],{"class":1918},[1736,54264,6663],{"class":1912},[1736,54266,54267],{"class":1738,"line":1784},[1736,54268,54269],{"class":1912},"      click me\n",[1736,54271,54272,54274,54276,54278,54280],{"class":1738,"line":1790},[1736,54273,6710],{"class":1912},[1736,54275,13031],{"class":6696},[1736,54277,53841],{"class":1912},[1736,54279,13031],{"class":6696},[1736,54281,6663],{"class":1912},[1736,54283,54284,54286,54288],{"class":1738,"line":1796},[1736,54285,6744],{"class":1912},[1736,54287,9089],{"class":1918},[1736,54289,6663],{"class":1912},[1736,54291,54292],{"class":1738,"line":2353},[1736,54293,6753],{"class":1912},[1736,54295,54296],{"class":1738,"line":2358},[1736,54297,1976],{"class":1912},[138,54299,54301,54302],{"id":54300},"reactreactchild-reactreactchild","React.ReactChild | React.ReactChild",[1736,54303],{},[11,54305,54306],{},"Allows single or multiple React elements, strings or numbers",[299,54308,54310],{"className":8734,"code":54309,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: React.ReactChild\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport function Card() {\n  return \u003CButton>click me\u003C/Button>\n}\n\nexport function Card2() {\n  return (\n    \u003CButton>\n      click me\n      \u003Ci>svg icon\u003C/i>\n    \u003C/Button>\n  )\n}\n",[179,54311,54312,54320,54332,54336,54368,54372,54382,54396,54400,54404,54414,54420,54428,54432,54444,54452,54456],{"__ignoreMap":307},[1736,54313,54314,54316,54318],{"class":1738,"line":1739},[1736,54315,53606],{"class":4866},[1736,54317,53609],{"class":2674},[1736,54319,4914],{"class":1912},[1736,54321,54322,54324,54326,54328,54330],{"class":1738,"line":748},[1736,54323,14140],{"class":5036},[1736,54325,1087],{"class":4866},[1736,54327,6649],{"class":2674},[1736,54329,891],{"class":1912},[1736,54331,54095],{"class":2674},[1736,54333,54334],{"class":1738,"line":756},[1736,54335,1976],{"class":1912},[1736,54337,54338,54340,54342,54344,54346,54348,54350,54352,54354,54356,54358,54360,54362,54364,54366],{"class":1738,"line":1755},[1736,54339,5029],{"class":4866},[1736,54341,30443],{"class":2674},[1736,54343,4911],{"class":4866},[1736,54345,17735],{"class":1912},[1736,54347,31760],{"class":5036},[1736,54349,7239],{"class":1912},[1736,54351,1087],{"class":4866},[1736,54353,53609],{"class":2674},[1736,54355,8939],{"class":1912},[1736,54357,7013],{"class":4866},[1736,54359,10193],{"class":1912},[1736,54361,14849],{"class":6696},[1736,54363,31779],{"class":1912},[1736,54365,14849],{"class":6696},[1736,54367,6663],{"class":1912},[1736,54369,54370],{"class":1738,"line":1761},[1736,54371,1747],{"emptyLinePlaceholder":790},[1736,54373,54374,54376,54378,54380],{"class":1738,"line":1767},[1736,54375,6632],{"class":4866},[1736,54377,6674],{"class":4866},[1736,54379,31559],{"class":2674},[1736,54381,6680],{"class":1912},[1736,54383,54384,54386,54388,54390,54392,54394],{"class":1738,"line":1772},[1736,54385,6685],{"class":4866},[1736,54387,10193],{"class":1912},[1736,54389,9089],{"class":1918},[1736,54391,30456],{"class":1912},[1736,54393,9089],{"class":1918},[1736,54395,6663],{"class":1912},[1736,54397,54398],{"class":1738,"line":1778},[1736,54399,1976],{"class":1912},[1736,54401,54402],{"class":1738,"line":1784},[1736,54403,1747],{"emptyLinePlaceholder":790},[1736,54405,54406,54408,54410,54412],{"class":1738,"line":1790},[1736,54407,6632],{"class":4866},[1736,54409,6674],{"class":4866},[1736,54411,54006],{"class":2674},[1736,54413,6680],{"class":1912},[1736,54415,54416,54418],{"class":1738,"line":1796},[1736,54417,6685],{"class":4866},[1736,54419,6688],{"class":1912},[1736,54421,54422,54424,54426],{"class":1738,"line":2353},[1736,54423,6693],{"class":1912},[1736,54425,9089],{"class":1918},[1736,54427,6663],{"class":1912},[1736,54429,54430],{"class":1738,"line":2358},[1736,54431,54269],{"class":1912},[1736,54433,54434,54436,54438,54440,54442],{"class":1738,"line":2364},[1736,54435,6710],{"class":1912},[1736,54437,13031],{"class":6696},[1736,54439,53841],{"class":1912},[1736,54441,13031],{"class":6696},[1736,54443,6663],{"class":1912},[1736,54445,54446,54448,54450],{"class":1738,"line":2370},[1736,54447,6744],{"class":1912},[1736,54449,9089],{"class":1918},[1736,54451,6663],{"class":1912},[1736,54453,54454],{"class":1738,"line":2376},[1736,54455,6753],{"class":1912},[1736,54457,54458],{"class":1738,"line":2381},[1736,54459,1976],{"class":1912},[138,54461,54463],{"id":54462},"reactreactnode","React.ReactNode",[11,54465,54466],{},"Allows multiple children, strings, numbers, fragments, portals... We could use the above example but it is a little verbose and ReactNode covers a little more.",[299,54468,54470],{"className":8734,"code":54469,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  children: React.ReactNode\n}\nconst Button = ({ children }: ButtonProps) => \u003Cbutton>{children}\u003C/button>\n\nexport default function Card() {\n  return (\n    \u003CButton>\n      click me\n      \u003Ci>svg icon\u003C/i>\n    \u003C/Button>\n  )\n}\n",[179,54471,54472,54480,54493,54497,54529,54533,54545,54551,54559,54563,54575,54583,54587],{"__ignoreMap":307},[1736,54473,54474,54476,54478],{"class":1738,"line":1739},[1736,54475,53606],{"class":4866},[1736,54477,53609],{"class":2674},[1736,54479,4914],{"class":1912},[1736,54481,54482,54484,54486,54488,54490],{"class":1738,"line":748},[1736,54483,14140],{"class":5036},[1736,54485,1087],{"class":4866},[1736,54487,6649],{"class":2674},[1736,54489,891],{"class":1912},[1736,54491,54492],{"class":2674},"ReactNode\n",[1736,54494,54495],{"class":1738,"line":756},[1736,54496,1976],{"class":1912},[1736,54498,54499,54501,54503,54505,54507,54509,54511,54513,54515,54517,54519,54521,54523,54525,54527],{"class":1738,"line":1755},[1736,54500,5029],{"class":4866},[1736,54502,30443],{"class":2674},[1736,54504,4911],{"class":4866},[1736,54506,17735],{"class":1912},[1736,54508,31760],{"class":5036},[1736,54510,7239],{"class":1912},[1736,54512,1087],{"class":4866},[1736,54514,53609],{"class":2674},[1736,54516,8939],{"class":1912},[1736,54518,7013],{"class":4866},[1736,54520,10193],{"class":1912},[1736,54522,14849],{"class":6696},[1736,54524,31779],{"class":1912},[1736,54526,14849],{"class":6696},[1736,54528,6663],{"class":1912},[1736,54530,54531],{"class":1738,"line":1761},[1736,54532,1747],{"emptyLinePlaceholder":790},[1736,54534,54535,54537,54539,54541,54543],{"class":1738,"line":1767},[1736,54536,6632],{"class":4866},[1736,54538,30438],{"class":4866},[1736,54540,6674],{"class":4866},[1736,54542,31559],{"class":2674},[1736,54544,6680],{"class":1912},[1736,54546,54547,54549],{"class":1738,"line":1772},[1736,54548,6685],{"class":4866},[1736,54550,6688],{"class":1912},[1736,54552,54553,54555,54557],{"class":1738,"line":1778},[1736,54554,6693],{"class":1912},[1736,54556,9089],{"class":1918},[1736,54558,6663],{"class":1912},[1736,54560,54561],{"class":1738,"line":1784},[1736,54562,54269],{"class":1912},[1736,54564,54565,54567,54569,54571,54573],{"class":1738,"line":1790},[1736,54566,6710],{"class":1912},[1736,54568,13031],{"class":6696},[1736,54570,53841],{"class":1912},[1736,54572,13031],{"class":6696},[1736,54574,6663],{"class":1912},[1736,54576,54577,54579,54581],{"class":1738,"line":1796},[1736,54578,6744],{"class":1912},[1736,54580,9089],{"class":1918},[1736,54582,6663],{"class":1912},[1736,54584,54585],{"class":1738,"line":2353},[1736,54586,6753],{"class":1912},[1736,54588,54589],{"class":1738,"line":2358},[1736,54590,1976],{"class":1912},[23,54592,3294],{"id":3293},[11,54594,54595],{},"And that's it, you now have no excuses for adding type checking for your children.",[23,54597,5706],{"id":5705},[70,54599,54600,54607,54614,54621],{},[73,54601,54602],{},[15,54603,54606],{"href":54604,"rel":54605},"https://frontendmasters.com/courses/react-typescript/",[19],"React and TypeScript Course by Steve Kinney",[73,54608,54609],{},[15,54610,54613],{"href":54611,"rel":54612},"https://frontendmasters.com/courses/typescript-v3/",[19],"TypeScript Fundamentals, v3 course by Mike North",[73,54615,54616],{},[15,54617,54620],{"href":54618,"rel":54619},"https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html",[19],"Typescript docs",[73,54622,54623],{},[15,54624,54627],{"href":54625,"rel":54626},"https://www.smashingmagazine.com/printed-books/typescript-in-50-lessons/",[19],"TypeScript in 50 lessons",[2011,54629,54630],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":54632},[54633,54634,54635,54636,54637,54638,54639,54640,54641],{"id":53592,"depth":756,"text":53593},{"id":53721,"depth":756,"text":53593},{"id":53864,"depth":756,"text":53865},{"id":54065,"depth":756,"text":54066},{"id":54168,"depth":756,"text":54066},{"id":54300,"depth":756,"text":54301},{"id":54462,"depth":756,"text":54463},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2022-01-11","photo-1504438190342-5951e134ffee?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80",{},"/blog/typescript-and-children",{"title":53578,"description":53583},"blog/typescript-and-children",[15559,5221],"byAcZb7p0esSh3rlmwQEvrEYcHHvTFUzVQD_2x43JGk",{"id":54651,"title":54652,"body":54653,"canonical":788,"date":54657,"description":54658,"extension":786,"featured":787,"image":54659,"meta":54660,"navigation":790,"ogimage":788,"path":54661,"provider":3460,"published":787,"seo":54662,"stem":54663,"tags":54664,"url":54665,"__hash__":54666},"blog/blog/ui-mode-playwright.md","Playwright's UI Mode - watch mode and time travel debugging",{"type":8,"value":54654,"toc":54655},[],{"title":307,"searchDepth":748,"depth":748,"links":54656},[],"2023-05-05","Are you looking for a more efficient way to execute and debug your end to end tests? Look no further than Playwright's UI Mode. In this guide, we'll explore the features of Playwright's UI Mode and show you how to take advantage of them for your test automation needs.","v1630862642/debbie.codes/featured-posts/ui-mode_tgv3o4",{"platform":5508},"/blog/ui-mode-playwright",{"title":54652,"description":54658},"blog/ui-mode-playwright",[1412,1411],"https://dev.to/playwright/playwrights-ui-mode-watch-mode-and-time-travel-debugging-10g5","iswdcmnC2hS9Q50m03dvp-gSVIzFfBxre4hsyU9zXQY",{"id":54668,"title":54669,"body":54670,"canonical":788,"date":55846,"description":55847,"extension":786,"featured":787,"image":55848,"meta":55849,"navigation":790,"ogimage":788,"path":55850,"provider":5235,"published":787,"seo":55851,"stem":55852,"tags":55853,"url":788,"__hash__":55854},"blog/blog/understanding-typescript.md","Understanding TypeScript",{"type":8,"value":54671,"toc":55831},[54672,54675,54678,54682,54685,54688,54691,54695,54698,54741,54744,54748,54757,54760,54784,54788,54791,54828,54831,54901,54905,54908,54943,54946,54980,54983,54987,54990,54993,55023,55026,55064,55075,55122,55125,55179,55183,55186,55189,55229,55232,55300,55303,55370,55374,55377,55527,55531,55534,55659,55663,55666,55799,55801,55804,55806,55829],[11,54673,54674],{},"TypeScript is a superset of JavaScript. Any types that are added are not part of the final bundle so really TypeScript is there to make your life easier as a developer. It took me years to accept TypeScript. I always wanted to learn it. It was on my long list of todos but I found it complicated and unnecessary and therefore chose not to prioritize it and most importantly not to use it in any of my projects.",[11,54676,54677],{},"Then it got forced upon me and I had no choice but to learn it while at the same time learning a new framework. That was very hard indeed as I wasn't sure if I was learning something React or something TypeScript. Separating the two would have been a whole lot easier.",[23,54679,54681],{"id":54680},"why-typescript","Why TypeScript?",[11,54683,54684],{},"But I have to say that once you understand even some of the basics of TypeScript and start to use it in your code it really does make your life easier.",[11,54686,54687],{},"TypeScript is not there to complicate things but to help you not make mistakes. Yes it will scream at you and underline everything in red but it's telling you there is a problem and that if you don't fix it then probably something will break either now or in the future. This is actually really helpful and prevents bugs.",[11,54689,54690],{},"In JavaScript we would just allow anything to happen and fix it later which is never a good idea really. I actually believe that TypeScript should be introduced early in the learning of JavaScript cause trust me, once you use it you won't go back and today more and more codebases are being rewritten in TypeScript so it is by far the future.",[23,54692,54694],{"id":54693},"what-exactly-does-typescript-do","What exactly does TypeScript do?",[11,54696,54697],{},"TypeScript is about checking your types. What do I mean by that? When you have props that you pass down into your components, for example a name prop then this prop should only accept a string. If someone passes it a number then TypeScript will simply not allow it as it checks the types and a number is not the same type as a string. We basically need to define these types when we are passing our props.",[299,54699,54701],{"className":4894,"code":54700,"language":4896,"meta":307,"style":307},"const Button = (props: { buttonText: string }) => ({\n  \u003Cbutton>{props.buttonText}\u003C/button>\n})\n",[179,54702,54703,54732,54737],{"__ignoreMap":307},[1736,54704,54705,54707,54709,54711,54713,54715,54717,54719,54722,54724,54726,54728,54730],{"class":1738,"line":1739},[1736,54706,5029],{"class":4866},[1736,54708,30443],{"class":2674},[1736,54710,4911],{"class":4866},[1736,54712,1095],{"class":1912},[1736,54714,31463],{"class":5036},[1736,54716,1087],{"class":4866},[1736,54718,7827],{"class":1912},[1736,54720,54721],{"class":5036},"buttonText",[1736,54723,1087],{"class":4866},[1736,54725,6841],{"class":1918},[1736,54727,7778],{"class":1912},[1736,54729,7013],{"class":4866},[1736,54731,41782],{"class":1912},[1736,54733,54734],{"class":1738,"line":748},[1736,54735,54736],{"class":1912},"  \u003Cbutton>{props.buttonText}\u003C/button>\n",[1736,54738,54739],{"class":1738,"line":756},[1736,54740,10582],{"class":1912},[11,54742,54743],{},"In the above example we pass down props and use the semicolon to define the props. The props are added as an object with the name of the prop followed by a semicolon and the type the prop is allowed to be, which in this case is a string.",[23,54745,54747],{"id":54746},"defining-types","Defining Types",[11,54749,54750,54751,54756],{},"In this post I will be using Types instead of ",[15,54752,54755],{"href":54753,"rel":54754},"https://www.typescriptlang.org/docs/handbook/2/objects.html",[19],"Interfaces"," which are very similar but have some small differences. There is no right or wrong here so use whatever works best for you.",[11,54758,54759],{},"Types can be defined as any of the primitive values:",[70,54761,54762,54764,54767,54770,54773,54778,54781],{},[73,54763,17712],{},[73,54765,54766],{},"number",[73,54768,54769],{},"boolean",[73,54771,54772],{},"[] an array",[73,54774,17712,54775,54777],{},[1736,54776],{},", an array of strings (change to number for an array of numbers etc)",[73,54779,54780],{},"\"primary\" | \"secondary\", set a specific value",[73,54782,54783],{},"{}, any type of object",[138,54785,54787],{"id":54786},"defining-object-types","Defining Object Types",[11,54789,54790],{},"You can get even deeper here and define the shape of an object, for example an object that takes an id of string and a title of string or an array of object types",[299,54792,54794],{"className":8734,"code":54793,"language":8736,"meta":307,"style":307},"type Items = {\n  id: string\n  title: string\n}[]\n",[179,54795,54796,54807,54815,54823],{"__ignoreMap":307},[1736,54797,54798,54800,54803,54805],{"class":1738,"line":1739},[1736,54799,17368],{"class":4866},[1736,54801,54802],{"class":2674}," Items",[1736,54804,4911],{"class":4866},[1736,54806,4914],{"class":1912},[1736,54808,54809,54811,54813],{"class":1738,"line":748},[1736,54810,13683],{"class":5036},[1736,54812,1087],{"class":4866},[1736,54814,6866],{"class":1918},[1736,54816,54817,54819,54821],{"class":1738,"line":756},[1736,54818,35907],{"class":5036},[1736,54820,1087],{"class":4866},[1736,54822,6866],{"class":1918},[1736,54824,54825],{"class":1738,"line":1755},[1736,54826,54827],{"class":1912},"}[]\n",[11,54829,54830],{},"This can also be cleaned up further by creating a type of item an then passing that to the array",[299,54832,54834],{"className":25266,"code":54833,"language":25268,"meta":307,"style":307},"interface Item {\n  id: string\n  title: string\n}\n\ninterface ComponentProps {\n  item: item\n  items: items\n}\n",[179,54835,54836,54845,54853,54861,54865,54869,54878,54887,54897],{"__ignoreMap":307},[1736,54837,54838,54840,54843],{"class":1738,"line":1739},[1736,54839,53606],{"class":4866},[1736,54841,54842],{"class":2674}," Item",[1736,54844,4914],{"class":1912},[1736,54846,54847,54849,54851],{"class":1738,"line":748},[1736,54848,13683],{"class":5036},[1736,54850,1087],{"class":4866},[1736,54852,6866],{"class":1918},[1736,54854,54855,54857,54859],{"class":1738,"line":756},[1736,54856,35907],{"class":5036},[1736,54858,1087],{"class":4866},[1736,54860,6866],{"class":1918},[1736,54862,54863],{"class":1738,"line":1755},[1736,54864,1976],{"class":1912},[1736,54866,54867],{"class":1738,"line":1761},[1736,54868,1747],{"emptyLinePlaceholder":790},[1736,54870,54871,54873,54876],{"class":1738,"line":1767},[1736,54872,53606],{"class":4866},[1736,54874,54875],{"class":2674}," ComponentProps",[1736,54877,4914],{"class":1912},[1736,54879,54880,54882,54884],{"class":1738,"line":1772},[1736,54881,13739],{"class":5036},[1736,54883,1087],{"class":4866},[1736,54885,54886],{"class":2674}," item\n",[1736,54888,54889,54892,54894],{"class":1738,"line":1778},[1736,54890,54891],{"class":5036},"  items",[1736,54893,1087],{"class":4866},[1736,54895,54896],{"class":2674}," items\n",[1736,54898,54899],{"class":1738,"line":1784},[1736,54900,1976],{"class":1912},[138,54902,54904],{"id":54903},"defining-array-types","Defining Array Types",[11,54906,54907],{},"We can define an array where all keys have to be a number and the value has to be a string",[299,54909,54911],{"className":8734,"code":54910,"language":8736,"meta":307,"style":307},"interface Items {\n  [key: number]: string\n}\n",[179,54912,54913,54921,54939],{"__ignoreMap":307},[1736,54914,54915,54917,54919],{"class":1738,"line":1739},[1736,54916,53606],{"class":4866},[1736,54918,54802],{"class":2674},[1736,54920,4914],{"class":1912},[1736,54922,54923,54926,54928,54930,54932,54935,54937],{"class":1738,"line":748},[1736,54924,54925],{"class":1912},"  [",[1736,54927,31231],{"class":5036},[1736,54929,1087],{"class":4866},[1736,54931,8833],{"class":1918},[1736,54933,54934],{"class":1912},"]",[1736,54936,1087],{"class":4866},[1736,54938,6866],{"class":1918},[1736,54940,54941],{"class":1738,"line":756},[1736,54942,1976],{"class":1912},[11,54944,54945],{},"Or we could make all keys a string and the value has to be of the type Item which was previously defined.",[299,54947,54949],{"className":8734,"code":54948,"language":8736,"meta":307,"style":307},"interface Items {\n  [key: string]: Item\n}\n",[179,54950,54951,54959,54976],{"__ignoreMap":307},[1736,54952,54953,54955,54957],{"class":1738,"line":1739},[1736,54954,53606],{"class":4866},[1736,54956,54802],{"class":2674},[1736,54958,4914],{"class":1912},[1736,54960,54961,54963,54965,54967,54969,54971,54973],{"class":1738,"line":748},[1736,54962,54925],{"class":1912},[1736,54964,31231],{"class":5036},[1736,54966,1087],{"class":4866},[1736,54968,6841],{"class":1918},[1736,54970,54934],{"class":1912},[1736,54972,1087],{"class":4866},[1736,54974,54975],{"class":2674}," Item\n",[1736,54977,54978],{"class":1738,"line":756},[1736,54979,1976],{"class":1912},[11,54981,54982],{},"This is a pretty simple way to define the props but you can imagine it getting very hard to manage if there were multiple props so adding the types inline might become a nightmare. This is one of the main reasons why we separate the props",[138,54984,54986],{"id":54985},"defining-function-types","Defining Function Types",[11,54988,54989],{},"You can also define types for functions:",[11,54991,54992],{},"Function takes no arguments and does not return anything.",[299,54994,54996],{"className":8734,"code":54995,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  onHover: () => void\n}\n",[179,54997,54998,55006,55019],{"__ignoreMap":307},[1736,54999,55000,55002,55004],{"class":1738,"line":1739},[1736,55001,53606],{"class":4866},[1736,55003,53609],{"class":2674},[1736,55005,4914],{"class":1912},[1736,55007,55008,55011,55013,55015,55017],{"class":1738,"line":748},[1736,55009,55010],{"class":2674},"  onHover",[1736,55012,1087],{"class":4866},[1736,55014,7010],{"class":1912},[1736,55016,7013],{"class":4866},[1736,55018,8944],{"class":1918},[1736,55020,55021],{"class":1738,"line":756},[1736,55022,1976],{"class":1912},[11,55024,55025],{},"Passes in the id of type number and returns nothing, eg undefined",[299,55027,55029],{"className":8734,"code":55028,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  onChange: (id: number) => void\n}\n",[179,55030,55031,55039,55060],{"__ignoreMap":307},[1736,55032,55033,55035,55037],{"class":1738,"line":1739},[1736,55034,53606],{"class":4866},[1736,55036,53609],{"class":2674},[1736,55038,4914],{"class":1912},[1736,55040,55041,55044,55046,55048,55050,55052,55054,55056,55058],{"class":1738,"line":748},[1736,55042,55043],{"class":2674},"  onChange",[1736,55045,1087],{"class":4866},[1736,55047,1095],{"class":1912},[1736,55049,7167],{"class":5036},[1736,55051,1087],{"class":4866},[1736,55053,8833],{"class":1918},[1736,55055,8939],{"class":1912},[1736,55057,7013],{"class":4866},[1736,55059,8944],{"class":1918},[1736,55061,55062],{"class":1738,"line":756},[1736,55063,1976],{"class":1912},[11,55065,55066,55067,55070,55071,55074],{},"Takes an event that is based on clicking the button and returns nothing. Notice the ",[179,55068,55069],{},"\u003CHTMLButtonElement>",", this means pass in all the available props that the HTML Button provides so it knows you might want to have access to",[179,55072,55073],{},"event.target"," for example.",[299,55076,55078],{"className":8734,"code":55077,"language":8736,"meta":307,"style":307},"interface ButtonProps {\n  onClick(event: React.MouseEvent\u003CHTMLButtonElement>): void\n}\n",[179,55079,55080,55088,55118],{"__ignoreMap":307},[1736,55081,55082,55084,55086],{"class":1738,"line":1739},[1736,55083,53606],{"class":4866},[1736,55085,53609],{"class":2674},[1736,55087,4914],{"class":1912},[1736,55089,55090,55093,55095,55098,55100,55102,55104,55107,55109,55112,55114,55116],{"class":1738,"line":748},[1736,55091,55092],{"class":2674},"  onClick",[1736,55094,7751],{"class":1912},[1736,55096,55097],{"class":5036},"event",[1736,55099,1087],{"class":4866},[1736,55101,6649],{"class":2674},[1736,55103,891],{"class":1912},[1736,55105,55106],{"class":2674},"MouseEvent",[1736,55108,6657],{"class":1912},[1736,55110,55111],{"class":2674},"HTMLButtonElement",[1736,55113,14293],{"class":1912},[1736,55115,1087],{"class":4866},[1736,55117,8944],{"class":1918},[1736,55119,55120],{"class":1738,"line":756},[1736,55121,1976],{"class":1912},[11,55123,55124],{},"When creating functions we can even define the types of what gets passed in as well as what gets returned. However TypeScript is clever enough that it knows if you pass in a as a number and b as a number and you and you return a + b then the return value will be a number so there is not need to define that.",[299,55126,55128],{"className":8734,"code":55127,"language":8736,"meta":307,"style":307},"const add = (a: number, b: number): number => {\n  return a + b\n}\n",[179,55129,55130,55164,55175],{"__ignoreMap":307},[1736,55131,55132,55134,55136,55138,55140,55142,55144,55146,55148,55150,55152,55154,55156,55158,55160,55162],{"class":1738,"line":1739},[1736,55133,5029],{"class":4866},[1736,55135,2681],{"class":2674},[1736,55137,4911],{"class":4866},[1736,55139,1095],{"class":1912},[1736,55141,15],{"class":5036},[1736,55143,1087],{"class":4866},[1736,55145,8833],{"class":1918},[1736,55147,829],{"class":1912},[1736,55149,16687],{"class":5036},[1736,55151,1087],{"class":4866},[1736,55153,8833],{"class":1918},[1736,55155,952],{"class":1912},[1736,55157,1087],{"class":4866},[1736,55159,8833],{"class":1918},[1736,55161,10208],{"class":4866},[1736,55163,4914],{"class":1912},[1736,55165,55166,55168,55170,55172],{"class":1738,"line":748},[1736,55167,6685],{"class":4866},[1736,55169,16782],{"class":1912},[1736,55171,9343],{"class":4866},[1736,55173,55174],{"class":1912}," b\n",[1736,55176,55177],{"class":1738,"line":756},[1736,55178,1976],{"class":1912},[23,55180,55182],{"id":55181},"separating-your-types","Separating your Types",[11,55184,55185],{},"We previously added our button with the buttonText prop containing the type of string. If our button has more types this would be really hard to maintain. This is one reason why we separate our types from here but also if we do separate them we can then export them and use them in other components.",[11,55187,55188],{},"From this:",[299,55190,55191],{"className":4894,"code":54700,"language":4896,"meta":307,"style":307},[179,55192,55193,55221,55225],{"__ignoreMap":307},[1736,55194,55195,55197,55199,55201,55203,55205,55207,55209,55211,55213,55215,55217,55219],{"class":1738,"line":1739},[1736,55196,5029],{"class":4866},[1736,55198,30443],{"class":2674},[1736,55200,4911],{"class":4866},[1736,55202,1095],{"class":1912},[1736,55204,31463],{"class":5036},[1736,55206,1087],{"class":4866},[1736,55208,7827],{"class":1912},[1736,55210,54721],{"class":5036},[1736,55212,1087],{"class":4866},[1736,55214,6841],{"class":1918},[1736,55216,7778],{"class":1912},[1736,55218,7013],{"class":4866},[1736,55220,41782],{"class":1912},[1736,55222,55223],{"class":1738,"line":748},[1736,55224,54736],{"class":1912},[1736,55226,55227],{"class":1738,"line":756},[1736,55228,10582],{"class":1912},[11,55230,55231],{},"To this:",[299,55233,55235],{"className":4894,"code":55234,"language":4896,"meta":307,"style":307},"export type ButtonProps = {\n  buttonText: string\n}\n\n\nconst Button = (props: ButtonProps) => ({\n  \u003Cbutton>{props.buttonText}\u003C/button>\n})\n",[179,55236,55237,55249,55258,55262,55266,55270,55292,55296],{"__ignoreMap":307},[1736,55238,55239,55241,55243,55245,55247],{"class":1738,"line":1739},[1736,55240,6632],{"class":4866},[1736,55242,6635],{"class":4866},[1736,55244,53609],{"class":2674},[1736,55246,4911],{"class":4866},[1736,55248,4914],{"class":1912},[1736,55250,55251,55254,55256],{"class":1738,"line":748},[1736,55252,55253],{"class":5036},"  buttonText",[1736,55255,1087],{"class":4866},[1736,55257,6866],{"class":1918},[1736,55259,55260],{"class":1738,"line":756},[1736,55261,1976],{"class":1912},[1736,55263,55264],{"class":1738,"line":1755},[1736,55265,1747],{"emptyLinePlaceholder":790},[1736,55267,55268],{"class":1738,"line":1761},[1736,55269,1747],{"emptyLinePlaceholder":790},[1736,55271,55272,55274,55276,55278,55280,55282,55284,55286,55288,55290],{"class":1738,"line":1767},[1736,55273,5029],{"class":4866},[1736,55275,30443],{"class":2674},[1736,55277,4911],{"class":4866},[1736,55279,1095],{"class":1912},[1736,55281,31463],{"class":5036},[1736,55283,1087],{"class":4866},[1736,55285,53609],{"class":2674},[1736,55287,8939],{"class":1912},[1736,55289,7013],{"class":4866},[1736,55291,41782],{"class":1912},[1736,55293,55294],{"class":1738,"line":1772},[1736,55295,54736],{"class":1912},[1736,55297,55298],{"class":1738,"line":1778},[1736,55299,10582],{"class":1912},[11,55301,55302],{},"Again we can improve it further by using the names of the props instead of using the props keyword and adding them in curly brackets to destructure them.",[299,55304,55306],{"className":4894,"code":55305,"language":4896,"meta":307,"style":307},"export type ButtonProps = {\n  buttonText: string\n}\n\nconst Button = ({buttonText}: ButtonProps) => ({\n  \u003Cbutton>{buttonText}\u003C/button>\n})\n",[179,55307,55308,55320,55328,55332,55336,55361,55366],{"__ignoreMap":307},[1736,55309,55310,55312,55314,55316,55318],{"class":1738,"line":1739},[1736,55311,6632],{"class":4866},[1736,55313,6635],{"class":4866},[1736,55315,53609],{"class":2674},[1736,55317,4911],{"class":4866},[1736,55319,4914],{"class":1912},[1736,55321,55322,55324,55326],{"class":1738,"line":748},[1736,55323,55253],{"class":5036},[1736,55325,1087],{"class":4866},[1736,55327,6866],{"class":1918},[1736,55329,55330],{"class":1738,"line":756},[1736,55331,1976],{"class":1912},[1736,55333,55334],{"class":1738,"line":1755},[1736,55335,1747],{"emptyLinePlaceholder":790},[1736,55337,55338,55340,55342,55344,55347,55349,55351,55353,55355,55357,55359],{"class":1738,"line":1761},[1736,55339,5029],{"class":4866},[1736,55341,30443],{"class":2674},[1736,55343,4911],{"class":4866},[1736,55345,55346],{"class":1912}," ({",[1736,55348,54721],{"class":5036},[1736,55350,9053],{"class":1912},[1736,55352,1087],{"class":4866},[1736,55354,53609],{"class":2674},[1736,55356,8939],{"class":1912},[1736,55358,7013],{"class":4866},[1736,55360,41782],{"class":1912},[1736,55362,55363],{"class":1738,"line":1767},[1736,55364,55365],{"class":1912},"  \u003Cbutton>{buttonText}\u003C/button>\n",[1736,55367,55368],{"class":1738,"line":1772},[1736,55369,10582],{"class":1912},[23,55371,55373],{"id":55372},"optional-props","Optional Props",[11,55375,55376],{},"To make props optional we can add a question mark to the end of the type. TypeScript will then only check it's type if it is passed in.",[299,55378,55380],{"className":4894,"code":55379,"language":4896,"meta":307,"style":307},"export type ButtonProps = {\n  buttonText: string\n  variation?: 'primary' | 'secondary'\n}\n\n\nconst Button = ({buttonText}: ButtonProps) => ({\n  \u003Cbutton>{buttonText}\u003C/button>\n})\n\n// or\n\nconst Button = ({buttonText, variation}: ButtonProps) => ({\n  \u003Cbutton variation=\"primary\">{buttonText}\u003C/button>\n})\n",[179,55381,55382,55394,55402,55417,55421,55425,55429,55453,55457,55461,55465,55469,55473,55502,55523],{"__ignoreMap":307},[1736,55383,55384,55386,55388,55390,55392],{"class":1738,"line":1739},[1736,55385,6632],{"class":4866},[1736,55387,6635],{"class":4866},[1736,55389,53609],{"class":2674},[1736,55391,4911],{"class":4866},[1736,55393,4914],{"class":1912},[1736,55395,55396,55398,55400],{"class":1738,"line":748},[1736,55397,55253],{"class":5036},[1736,55399,1087],{"class":4866},[1736,55401,6866],{"class":1918},[1736,55403,55404,55407,55409,55412,55414],{"class":1738,"line":756},[1736,55405,55406],{"class":5036},"  variation",[1736,55408,8830],{"class":4866},[1736,55410,55411],{"class":1935}," 'primary'",[1736,55413,35748],{"class":4866},[1736,55415,55416],{"class":1935}," 'secondary'\n",[1736,55418,55419],{"class":1738,"line":1755},[1736,55420,1976],{"class":1912},[1736,55422,55423],{"class":1738,"line":1761},[1736,55424,1747],{"emptyLinePlaceholder":790},[1736,55426,55427],{"class":1738,"line":1767},[1736,55428,1747],{"emptyLinePlaceholder":790},[1736,55430,55431,55433,55435,55437,55439,55441,55443,55445,55447,55449,55451],{"class":1738,"line":1772},[1736,55432,5029],{"class":4866},[1736,55434,30443],{"class":2674},[1736,55436,4911],{"class":4866},[1736,55438,55346],{"class":1912},[1736,55440,54721],{"class":5036},[1736,55442,9053],{"class":1912},[1736,55444,1087],{"class":4866},[1736,55446,53609],{"class":2674},[1736,55448,8939],{"class":1912},[1736,55450,7013],{"class":4866},[1736,55452,41782],{"class":1912},[1736,55454,55455],{"class":1738,"line":1778},[1736,55456,55365],{"class":1912},[1736,55458,55459],{"class":1738,"line":1784},[1736,55460,10582],{"class":1912},[1736,55462,55463],{"class":1738,"line":1790},[1736,55464,1747],{"emptyLinePlaceholder":790},[1736,55466,55467],{"class":1738,"line":1796},[1736,55468,31373],{"class":6820},[1736,55470,55471],{"class":1738,"line":2353},[1736,55472,1747],{"emptyLinePlaceholder":790},[1736,55474,55475,55477,55479,55481,55483,55485,55487,55490,55492,55494,55496,55498,55500],{"class":1738,"line":2358},[1736,55476,5029],{"class":4866},[1736,55478,30443],{"class":2674},[1736,55480,4911],{"class":4866},[1736,55482,55346],{"class":1912},[1736,55484,54721],{"class":5036},[1736,55486,829],{"class":1912},[1736,55488,55489],{"class":5036},"variation",[1736,55491,9053],{"class":1912},[1736,55493,1087],{"class":4866},[1736,55495,53609],{"class":2674},[1736,55497,8939],{"class":1912},[1736,55499,7013],{"class":4866},[1736,55501,41782],{"class":1912},[1736,55503,55504,55507,55509,55512,55514,55517,55519,55521],{"class":1738,"line":2364},[1736,55505,55506],{"class":1912},"  \u003Cbutton variation",[1736,55508,5062],{"class":4866},[1736,55510,55511],{"class":1935},"\"primary\"",[1736,55513,9407],{"class":4866},[1736,55515,55516],{"class":1912},"{buttonText}",[1736,55518,8105],{"class":4866},[1736,55520,14849],{"class":1912},[1736,55522,6663],{"class":4866},[1736,55524,55525],{"class":1738,"line":2370},[1736,55526,10582],{"class":1912},[23,55528,55530],{"id":55529},"improving-your-props-with-comments","Improving your Props with Comments",[11,55532,55533],{},"Comments are a great way to help others understand what your props are for.",[299,55535,55537],{"className":4894,"code":55536,"language":4896,"meta":307,"style":307},"export type ButtonProps = {\n  /**\n   * a text for the button\n   */\n  buttonText: string\n  /**\n   * the variation of the button\n   */\n  variation?: 'primary' | 'secondary'\n}\n\n\nconst Button = ({buttonText, variation}: ButtonProps) => ({\n  \u003Cbutton variation=\"primary\">{buttonText}\u003C/button>\n})\n",[179,55538,55539,55551,55555,55560,55564,55572,55576,55581,55585,55597,55601,55605,55609,55637,55655],{"__ignoreMap":307},[1736,55540,55541,55543,55545,55547,55549],{"class":1738,"line":1739},[1736,55542,6632],{"class":4866},[1736,55544,6635],{"class":4866},[1736,55546,53609],{"class":2674},[1736,55548,4911],{"class":4866},[1736,55550,4914],{"class":1912},[1736,55552,55553],{"class":1738,"line":748},[1736,55554,6821],{"class":6820},[1736,55556,55557],{"class":1738,"line":756},[1736,55558,55559],{"class":6820},"   * a text for the button\n",[1736,55561,55562],{"class":1738,"line":1755},[1736,55563,6831],{"class":6820},[1736,55565,55566,55568,55570],{"class":1738,"line":1761},[1736,55567,55253],{"class":5036},[1736,55569,1087],{"class":4866},[1736,55571,6866],{"class":1918},[1736,55573,55574],{"class":1738,"line":1767},[1736,55575,6821],{"class":6820},[1736,55577,55578],{"class":1738,"line":1772},[1736,55579,55580],{"class":6820},"   * the variation of the button\n",[1736,55582,55583],{"class":1738,"line":1778},[1736,55584,6831],{"class":6820},[1736,55586,55587,55589,55591,55593,55595],{"class":1738,"line":1784},[1736,55588,55406],{"class":5036},[1736,55590,8830],{"class":4866},[1736,55592,55411],{"class":1935},[1736,55594,35748],{"class":4866},[1736,55596,55416],{"class":1935},[1736,55598,55599],{"class":1738,"line":1790},[1736,55600,1976],{"class":1912},[1736,55602,55603],{"class":1738,"line":1796},[1736,55604,1747],{"emptyLinePlaceholder":790},[1736,55606,55607],{"class":1738,"line":2353},[1736,55608,1747],{"emptyLinePlaceholder":790},[1736,55610,55611,55613,55615,55617,55619,55621,55623,55625,55627,55629,55631,55633,55635],{"class":1738,"line":2358},[1736,55612,5029],{"class":4866},[1736,55614,30443],{"class":2674},[1736,55616,4911],{"class":4866},[1736,55618,55346],{"class":1912},[1736,55620,54721],{"class":5036},[1736,55622,829],{"class":1912},[1736,55624,55489],{"class":5036},[1736,55626,9053],{"class":1912},[1736,55628,1087],{"class":4866},[1736,55630,53609],{"class":2674},[1736,55632,8939],{"class":1912},[1736,55634,7013],{"class":4866},[1736,55636,41782],{"class":1912},[1736,55638,55639,55641,55643,55645,55647,55649,55651,55653],{"class":1738,"line":2364},[1736,55640,55506],{"class":1912},[1736,55642,5062],{"class":4866},[1736,55644,55511],{"class":1935},[1736,55646,9407],{"class":4866},[1736,55648,55516],{"class":1912},[1736,55650,8105],{"class":4866},[1736,55652,14849],{"class":1912},[1736,55654,6663],{"class":4866},[1736,55656,55657],{"class":1738,"line":2370},[1736,55658,10582],{"class":1912},[23,55660,55662],{"id":55661},"adding-default-values","Adding default Values",[11,55664,55665],{},"Default values can be added to your props by giving it a value as you pass it into the function. Then if someone doesn't define a value for that prop the default will be used.",[299,55667,55669],{"className":4894,"code":55668,"language":4896,"meta":307,"style":307},"export type ButtonProps = {\n  /**\n   * a text for the button\n   */\n  buttonText: string\n  /**\n   * the variation of the button\n   */\n  variation?: 'primary' | 'secondary'\n}\n\n// add to cart is the default value of the button text\n\nconst Button = ({buttonText = \"add to cart\", variation}: ButtonProps) => ({\n  \u003Cbutton variation=\"primary\">{buttonText}\u003C/button>\n})\n",[179,55670,55671,55683,55687,55691,55695,55703,55707,55711,55715,55727,55731,55735,55740,55744,55777,55795],{"__ignoreMap":307},[1736,55672,55673,55675,55677,55679,55681],{"class":1738,"line":1739},[1736,55674,6632],{"class":4866},[1736,55676,6635],{"class":4866},[1736,55678,53609],{"class":2674},[1736,55680,4911],{"class":4866},[1736,55682,4914],{"class":1912},[1736,55684,55685],{"class":1738,"line":748},[1736,55686,6821],{"class":6820},[1736,55688,55689],{"class":1738,"line":756},[1736,55690,55559],{"class":6820},[1736,55692,55693],{"class":1738,"line":1755},[1736,55694,6831],{"class":6820},[1736,55696,55697,55699,55701],{"class":1738,"line":1761},[1736,55698,55253],{"class":5036},[1736,55700,1087],{"class":4866},[1736,55702,6866],{"class":1918},[1736,55704,55705],{"class":1738,"line":1767},[1736,55706,6821],{"class":6820},[1736,55708,55709],{"class":1738,"line":1772},[1736,55710,55580],{"class":6820},[1736,55712,55713],{"class":1738,"line":1778},[1736,55714,6831],{"class":6820},[1736,55716,55717,55719,55721,55723,55725],{"class":1738,"line":1784},[1736,55718,55406],{"class":5036},[1736,55720,8830],{"class":4866},[1736,55722,55411],{"class":1935},[1736,55724,35748],{"class":4866},[1736,55726,55416],{"class":1935},[1736,55728,55729],{"class":1738,"line":1790},[1736,55730,1976],{"class":1912},[1736,55732,55733],{"class":1738,"line":1796},[1736,55734,1747],{"emptyLinePlaceholder":790},[1736,55736,55737],{"class":1738,"line":2353},[1736,55738,55739],{"class":6820},"// add to cart is the default value of the button text\n",[1736,55741,55742],{"class":1738,"line":2358},[1736,55743,1747],{"emptyLinePlaceholder":790},[1736,55745,55746,55748,55750,55752,55754,55756,55758,55761,55763,55765,55767,55769,55771,55773,55775],{"class":1738,"line":2364},[1736,55747,5029],{"class":4866},[1736,55749,30443],{"class":2674},[1736,55751,4911],{"class":4866},[1736,55753,55346],{"class":1912},[1736,55755,54721],{"class":5036},[1736,55757,4911],{"class":4866},[1736,55759,55760],{"class":1935}," \"add to cart\"",[1736,55762,829],{"class":1912},[1736,55764,55489],{"class":5036},[1736,55766,9053],{"class":1912},[1736,55768,1087],{"class":4866},[1736,55770,53609],{"class":2674},[1736,55772,8939],{"class":1912},[1736,55774,7013],{"class":4866},[1736,55776,41782],{"class":1912},[1736,55778,55779,55781,55783,55785,55787,55789,55791,55793],{"class":1738,"line":2370},[1736,55780,55506],{"class":1912},[1736,55782,5062],{"class":4866},[1736,55784,55511],{"class":1935},[1736,55786,9407],{"class":4866},[1736,55788,55516],{"class":1912},[1736,55790,8105],{"class":4866},[1736,55792,14849],{"class":1912},[1736,55794,6663],{"class":4866},[1736,55796,55797],{"class":1738,"line":2376},[1736,55798,10582],{"class":1912},[23,55800,3294],{"id":3293},[11,55802,55803],{},"TypeScript might take a bit of extra work at the start as you define your types but that little bit of extra work can really save you some time later on. I highly recommend giving it a try and just starting to slowly introduce it in your codebases.",[23,55805,5706],{"id":5705},[70,55807,55808,55815,55822],{},[73,55809,55810],{},[15,55811,55814],{"href":55812,"rel":55813},"https://www.typescriptlang.org/",[19],"TypeScript org",[73,55816,55817,55821],{},[15,55818,55820],{"href":54604,"rel":55819},[19],"React && TypeScript on FrontendMasters"," by Steve Kinney",[73,55823,55824],{},[15,55825,55828],{"href":55826,"rel":55827},"https://stevekinney.github.io/react-and-typescript/",[19],"React && TypeScript Course notes",[2011,55830,53554],{},{"title":307,"searchDepth":748,"depth":748,"links":55832},[55833,55834,55835,55840,55841,55842,55843,55844,55845],{"id":54680,"depth":748,"text":54681},{"id":54693,"depth":748,"text":54694},{"id":54746,"depth":748,"text":54747,"children":55836},[55837,55838,55839],{"id":54786,"depth":756,"text":54787},{"id":54903,"depth":756,"text":54904},{"id":54985,"depth":756,"text":54986},{"id":55181,"depth":748,"text":55182},{"id":55372,"depth":748,"text":55373},{"id":55529,"depth":748,"text":55530},{"id":55661,"depth":748,"text":55662},{"id":3293,"depth":748,"text":3294},{"id":5705,"depth":748,"text":5706},"2021-10-06","TypeScript is a superset of JavaScript. Any types that are added are not part of the final bundle so really TypeScript is there to make your life easier as a developer.","reserve/uZYSV4nuQeyq64azfVIn_15130980706_64134efc6e_o.jpg?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80",{},"/blog/understanding-typescript",{"title":54669,"description":55847},"blog/understanding-typescript",[15559,5221],"bnMxqEZtfUJv-laKeUXqwJcte8iBaEETsQpx_giwnxk",{"id":55856,"title":55857,"body":55858,"canonical":55934,"date":55942,"description":55943,"extension":786,"featured":787,"image":788,"meta":55944,"navigation":790,"ogimage":788,"path":55945,"provider":788,"published":790,"seo":55946,"stem":55947,"tags":55948,"url":788,"__hash__":55950},"blog/blog/vibe-coding-with-copilot-and-gemini.md","Vibe Coding with Copilot and Gemini 2.5 pro - Simplifying My Blog with AI-Assisted Refactoring",{"type":8,"value":55859,"toc":55936},[55860,55863,55866,55869,55873,55876,55890,55893,55897,55900,55903,55907,55910,55918,55920,55923,55926,55928],[11,55861,55862],{},"I have been wanting to share more content on my blog, but ironically, the process of publishing has been slowing me down.",[11,55864,55865],{},"One of the biggest points of friction was managing images. Whether I was searching for the right visuals, generating AI images, or uploading and optimizing them, the effort added more overhead than value. In a recent vibe coding session, I decided to remove images from the blog entirely and explore a content-first approach instead.",[11,55867,55868],{},"To streamline the changes, I brought in some AI assistance with Copilot in VS Code and Gemini 2.5 Pro.",[23,55870,55872],{"id":55871},"using-ai-for-real-world-development-tasks","Using AI for Real-World Development Tasks",[11,55874,55875],{},"This was not just an experiment in AI usage. It was an exploration of how well a coding assistant could help with a live production codebase. I had a few goals for the session:",[70,55877,55878,55881,55884,55887],{},[73,55879,55880],{},"Remove images from the homepage and blog post layouts",[73,55882,55883],{},"Adjust the featured post section to work well without images",[73,55885,55886],{},"Allow multiple featured posts to improve the layout",[73,55888,55889],{},"Maintain passing tests and catch any regressions",[11,55891,55892],{},"Copilot handled most of the layout changes with confidence. There were some hiccups, such as accidentally removing unrelated components, but thanks to having tests in place, I was able to identify those quickly and guide the AI to restore the correct content.",[23,55894,55896],{"id":55895},"the-role-of-tests-when-pairing-with-ai","The Role of Tests When Pairing with AI",[11,55898,55899],{},"This session was a good reminder that test coverage is essential when collaborating with an AI tool. It is easy for an assistant to refactor or delete something that seems unused, only to discover later that it was important for functionality.",[11,55901,55902],{},"I spent some time updating my locators to be more dynamic rather than tied to specific static text. That small change helped the tests remain reliable and more resilient to future changes.",[23,55904,55906],{"id":55905},"watch-the-full-coding-session","Watch the Full Coding Session",[11,55908,55909],{},"If you are curious about how Gemini 2.5 pro performed, you can watch the full coding session on YouTube. You will see the back-and-forth of using an AI assistant to remove image dependencies, refactor layouts, restore unintended changes, and iterate toward a cleaner and simpler blog experience.",[11,55911,55912,55913],{},"Watch here: ",[15,55914,55917],{"href":55915,"rel":55916},"https://www.youtube.com/watch?v=wqhnjI131rk",[19],"Vibe Coding with Copilot and Gemini 2.5: Removing Blog Images, Fixing Layouts, and Debugging with AI",[23,55919,25046],{"id":25045},[11,55921,55922],{},"This session reminded me that small barriers, like managing images, can hold back your momentum as a creator. By removing friction and getting help from AI, it becomes easier to ship improvements and stay focused on what matters.",[11,55924,55925],{},"If you have not tried coding with an AI assistant yet, this is a good example of how it can be helpful. It is not perfect, but it is a powerful partner when combined with tests and a clear workflow.",[731,55927],{},[11,55929,55930],{},[133,55931,25534,55932,891],{},[15,55933,25539],{"href":55934,"rel":55935},"https://dev.to/debs_obrien/vibe-coding-with-copilot-and-gemini-25-pro-simplifying-my-blog-with-ai-assisted-refactoring-3033",[19],{"title":307,"searchDepth":748,"depth":748,"links":55937},[55938,55939,55940,55941],{"id":55871,"depth":748,"text":55872},{"id":55895,"depth":748,"text":55896},{"id":55905,"depth":748,"text":55906},{"id":25045,"depth":748,"text":25046},"2025-06-20","I have been wanting to share more content on my blog, but ironically, the process of publishing has been slowing me down. In a recent vibe coding session, I decided to remove images from the blog entirely and explore a content-first approach with AI assistance.",{"loading":3458},"/blog/vibe-coding-with-copilot-and-gemini",{"title":55857,"description":55943},"blog/vibe-coding-with-copilot-and-gemini",[796,795,2724,55949],"vscode","yXQR2vhodedc73pMYEeQ5E1hI-nxfmRrsS6GIC0HfHo",{"id":55952,"title":55953,"body":55954,"canonical":788,"date":56268,"description":56269,"extension":786,"featured":787,"image":56270,"meta":56271,"navigation":790,"ogimage":788,"path":56272,"provider":5235,"published":790,"seo":56273,"stem":56274,"tags":56275,"url":788,"__hash__":56276},"blog/blog/vs-code-tips.md","VS Code Tips",{"type":8,"value":55955,"toc":56245},[55956,55960,55964,55967,55980,55983,55997,56001,56004,56017,56021,56024,56033,56037,56041,56056,56090,56094,56097,56101,56117,56147,56151,56160,56164,56172,56176,56183,56187,56195,56199,56207,56211,56219,56223,56232,56234,56243],[23,55957,55959],{"id":55958},"vs-code-shortcuts","VS Code Shortcuts",[138,55961,55963],{"id":55962},"select-all-words","Select all Words",[11,55965,55966],{},"Highlight a word and press Command D and it will select all the other instances of that word meaning you can edit that word and it will edit all of the other instances of that word at the same time. To select the next word",[299,55968,55970],{"className":2665,"code":55969,"language":2667,"meta":307,"style":307},"Command D\n",[179,55971,55972],{"__ignoreMap":307},[1736,55973,55974,55977],{"class":1738,"line":1739},[1736,55975,55976],{"class":2674},"Command",[1736,55978,55979],{"class":1935}," D\n",[11,55981,55982],{},"To select all words",[299,55984,55986],{"className":2665,"code":55985,"language":2667,"meta":307,"style":307},"Command shift D\n",[179,55987,55988],{"__ignoreMap":307},[1736,55989,55990,55992,55995],{"class":1738,"line":1739},[1736,55991,55976],{"class":2674},[1736,55993,55994],{"class":1935}," shift",[1736,55996,55979],{"class":1935},[23,55998,56000],{"id":55999},"multiple-cursers","Multiple cursers",[11,56002,56003],{},"Press option and click to have multiple cursers. If for example you want to edit or add text in various places you can do it all at once by pressing option and clicking in the various places and then typing.",[299,56005,56007],{"className":2665,"code":56006,"language":2667,"meta":307,"style":307},"Option Click\n",[179,56008,56009],{"__ignoreMap":307},[1736,56010,56011,56014],{"class":1738,"line":1739},[1736,56012,56013],{"class":2674},"Option",[1736,56015,56016],{"class":1935}," Click\n",[138,56018,56020],{"id":56019},"create-a-file-and-folder","Create a File and Folder",[11,56022,56023],{},"To create a file and folder at the same time just create a new file and add the folder name followed by a slash and then the file name.",[299,56025,56027],{"className":2665,"code":56026,"language":2667,"meta":307,"style":307},"components/button.tsx\n",[179,56028,56029],{"__ignoreMap":307},[1736,56030,56031],{"class":1738,"line":1739},[1736,56032,56026],{"class":2674},[23,56034,56036],{"id":56035},"vs-code-settings","VS Code Settings",[138,56038,56040],{"id":56039},"monospaced-fonts","Monospaced fonts",[11,56042,56043,56044,56049,56050,56055],{},"To use coll fonts that turn your symbols into real looking symbols you can use ",[15,56045,56048],{"href":56046,"rel":56047},"https://github.com/tonsky/FiraCode",[19],"Fira Code"," which is free. You can ",[15,56051,56054],{"href":56052,"rel":56053},"https://github.com/tonsky/FiraCode/wiki/VS-Code-Instructions",[19],"enable on VS-code"," by selecting the font in the settings editor under \"Commonly Used\" or you can add a '.vscode/settings.json` file.",[299,56057,56059],{"className":1903,"code":56058,"language":1905,"meta":307,"style":307},"{\n  \"editor.fontFamily\": \"Fira Code\",\n  \"editor.fontLigatures\": true\n}\n",[179,56060,56061,56065,56077,56086],{"__ignoreMap":307},[1736,56062,56063],{"class":1738,"line":1739},[1736,56064,1913],{"class":1912},[1736,56066,56067,56070,56072,56075],{"class":1738,"line":748},[1736,56068,56069],{"class":1918},"  \"editor.fontFamily\"",[1736,56071,3065],{"class":1912},[1736,56073,56074],{"class":1935},"\"Fira Code\"",[1736,56076,1939],{"class":1912},[1736,56078,56079,56082,56084],{"class":1738,"line":756},[1736,56080,56081],{"class":1918},"  \"editor.fontLigatures\"",[1736,56083,3065],{"class":1912},[1736,56085,21380],{"class":1918},[1736,56087,56088],{"class":1738,"line":1755},[1736,56089,1976],{"class":1912},[23,56091,56093],{"id":56092},"vs-code-extensions","VS Code Extensions",[11,56095,56096],{},"Here is a collection of some of my favorite VS Code extensions.",[138,56098,56100],{"id":56099},"add-a-spell-checker","Add a Spell checker",[11,56102,52700,56103,56108,56109,56112,56113,56116],{},[15,56104,56107],{"href":56105,"rel":56106},"https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker",[19],"spell checker Extension"," for VS Code and then add a ",[179,56110,56111],{},".vscode/cSpell.json"," file with an array of words for that project that you want the spell checker to ignore. Or you can go to settings, ",[179,56114,56115],{},"command ,"," search for spell add add the word to the \"C Spell: Dictionaries\" so it will be excluded from spellchecking for all projects.",[299,56118,56120],{"className":1903,"code":56119,"language":1905,"meta":307,"style":307},"{\n  \"words\": [\"Fira\", \"Monospaced\"]\n}\n",[179,56121,56122,56126,56143],{"__ignoreMap":307},[1736,56123,56124],{"class":1738,"line":1739},[1736,56125,1913],{"class":1912},[1736,56127,56128,56131,56133,56136,56138,56141],{"class":1738,"line":748},[1736,56129,56130],{"class":1918},"  \"words\"",[1736,56132,16439],{"class":1912},[1736,56134,56135],{"class":1935},"\"Fira\"",[1736,56137,829],{"class":1912},[1736,56139,56140],{"class":1935},"\"Monospaced\"",[1736,56142,8420],{"class":1912},[1736,56144,56145],{"class":1738,"line":756},[1736,56146,1976],{"class":1912},[138,56148,56150],{"id":56149},"error-lens","Error Lens",[11,56152,56153,56154,56159],{},"If you really want to highlight your errors then the ",[15,56155,56158],{"href":56156,"rel":56157},"https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens",[19],"Error Lens extension"," will be of great help. It will add the error to the end of the line so it is really easy to see and understand what the error is.",[138,56161,56163],{"id":56162},"add-folder-icons","Add folder icons",[11,56165,2506,56166,56171],{},[15,56167,56170],{"href":56168,"rel":56169},"https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme",[19],"Material Icon Theme extension"," will add icons to your folder structure which is just nice and makes finding folders easy to see.",[23,56173,56175],{"id":56174},"bracket-pair-colorizer","Bracket Pair Colorizer",[11,56177,2506,56178,56182],{},[15,56179,56175],{"href":56180,"rel":56181},"https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer",[19]," makes it so much easier to see where your brackets start and end by showing them in different colors so much easier to see when things are going wrong.",[138,56184,56186],{"id":56185},"formatting-toggle","Formatting Toggle",[11,56188,56189,56190,56194],{},"Sometimes you just don't want to format your code or prettier is doing things that you don't want it to so being easily able to turn it on and off is really useful. ",[15,56191,56186],{"href":56192,"rel":56193},"https://marketplace.visualstudio.com/items?itemName=tombonnike.vscode-status-bar-format-toggle",[19]," is a great extension.",[138,56196,56198],{"id":56197},"vs-code-live-share","VS Code Live Share",[11,56200,56201,56202,56206],{},"This has to be one of the best extensions out there. This gives you the ability to send a link to someone so they can jump into your VS Code editor and type away meaning they can help you debug your code. ",[15,56203,56198],{"href":56204,"rel":56205},"https://marketplace.visualstudio.com/items?itemName=MS-vsliveshare.vsliveshare",[19]," is a great way to pair program and you have have numerous people join the session.",[138,56208,56210],{"id":56209},"search-node-modules","Search Node Modules",[11,56212,56213,56214,56218],{},"This is one I have only recently started to use and it has been a life saver for when you have to debug anything inside node modules folder. ",[15,56215,56210],{"href":56216,"rel":56217},"https://marketplace.visualstudio.com/items?itemName=jasonnutter.search-node-modules",[19]," allows you to easily navigate the file inside your project's node_modules directory.",[138,56220,56222],{"id":56221},"tailwind-intellisense","Tailwind Intellisense",[11,56224,56225,56226,56231],{},"Of course if you are a TailwindCSS fan like me then this is a must. ",[15,56227,56230],{"href":56228,"rel":56229},"https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss",[19],"Tailwind CSS Intellisense"," gives you advanced features such as autocomplete, syntax highlighting, and linting.",[23,56233,3294],{"id":3293},[11,56235,56236,56237,56242],{},"Of course there are many other extensions that I have in use some of them are installed and probably never used but for sure the above are some of my favorite and ones I use all the time. Feel free to share yours with me on ",[15,56238,56241],{"href":56239,"rel":56240},"https://twitter.com/debs_obrien",[19],"twitter",". Maybe there is a cool extension out there that I am missing.",[2011,56244,37528],{},{"title":307,"searchDepth":748,"depth":748,"links":56246},[56247,56250,56253,56256,56261,56267],{"id":55958,"depth":748,"text":55959,"children":56248},[56249],{"id":55962,"depth":756,"text":55963},{"id":55999,"depth":748,"text":56000,"children":56251},[56252],{"id":56019,"depth":756,"text":56020},{"id":56035,"depth":748,"text":56036,"children":56254},[56255],{"id":56039,"depth":756,"text":56040},{"id":56092,"depth":748,"text":56093,"children":56257},[56258,56259,56260],{"id":56099,"depth":756,"text":56100},{"id":56149,"depth":756,"text":56150},{"id":56162,"depth":756,"text":56163},{"id":56174,"depth":748,"text":56175,"children":56262},[56263,56264,56265,56266],{"id":56185,"depth":756,"text":56186},{"id":56197,"depth":756,"text":56198},{"id":56209,"depth":756,"text":56210},{"id":56221,"depth":756,"text":56222},{"id":3293,"depth":748,"text":3294},"2021-05-13","Tips, shortcuts and extensions to make working with VS Code easier so you can developer faster and let the tools do the job for you.","photo-1510751007277-36932aac9ebd?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop",{"loading":3458},"/blog/vs-code-tips",{"title":55953,"description":56269},"blog/vs-code-tips",[22558],"j1eGaEsf6i40QAFjUR0huyxRRIKGkNxDPfcN9cVERMs",{"id":56278,"title":56279,"body":56280,"canonical":788,"date":56705,"description":56706,"extension":786,"featured":787,"image":56707,"meta":56708,"navigation":790,"ogimage":788,"path":56709,"provider":5235,"published":787,"seo":56710,"stem":56711,"tags":56712,"url":788,"__hash__":56713},"blog/blog/vue-directives.md","Vue Directives",{"type":8,"value":56281,"toc":56689},[56282,56285,56289,56295,56316,56320,56326,56346,56350,56366,56394,56398,56407,56428,56432,56438,56459,56468,56477,56481,56496,56522,56525,56549,56559,56568,56577,56583,56595,56601,56605,56608,56612,56618,56643,56647,56658,56660,56668,56674,56686],[11,56283,56284],{},"This taken from Sara Drasner's course on Frontend Masters. I did the original one when I was just learning Vue for the first time. Since then I have worked with Vue for a good few years and there are still things I am learning. These are my notes from the first few lessons. Hope it helps someone else learn something they didn't already know.",[23,56286,56288],{"id":56287},"lazy-models","Lazy Models",[11,56290,56291,56294],{},[179,56292,56293],{},"v-model.lazy"," for when you don't want to it to be always watching for changes and only modify the DOM when you click something for example when you type and then click and it appears on the page only when you click and not as you type.",[299,56296,56298],{"className":21974,"code":56297,"language":21976,"meta":307,"style":307},"\u003Cinput v-model.lazy=\"msg\" />\n",[179,56299,56300],{"__ignoreMap":307},[1736,56301,56302,56304,56306,56309,56311,56314],{"class":1738,"line":1739},[1736,56303,6657],{"class":1912},[1736,56305,43332],{"class":6696},[1736,56307,56308],{"class":2674}," v-model.lazy",[1736,56310,5062],{"class":1912},[1736,56312,56313],{"class":1935},"\"msg\"",[1736,56315,6739],{"class":1912},[23,56317,56319],{"id":56318},"trimming-your-models","Trimming your Models",[11,56321,56322,56325],{},[179,56323,56324],{},"v-modal-trim"," will automatically trim whitespace in your inputs.",[299,56327,56329],{"className":21974,"code":56328,"language":21976,"meta":307,"style":307},"\u003Cinput v-model.trim=\"msg\" />\n",[179,56330,56331],{"__ignoreMap":307},[1736,56332,56333,56335,56337,56340,56342,56344],{"class":1738,"line":1739},[1736,56334,6657],{"class":1912},[1736,56336,43332],{"class":6696},[1736,56338,56339],{"class":2674}," v-model.trim",[1736,56341,5062],{"class":1912},[1736,56343,56313],{"class":1935},[1736,56345,6739],{"class":1912},[23,56347,56349],{"id":56348},"making-your-models-a-number","Making your Models a Number",[11,56351,56352,56355,56356,56359,56360,56362,56363,891],{},[179,56353,56354],{},"v-model-number"," will convert any string to a number. We can use ",[179,56357,56358],{},"type=\"number\""," but the value of HTML input elements will always return a string and if it can't be parsed with parseFloat() the original value is returned. Therefore using ",[179,56361,56354],{}," ensures our input is of type ",[179,56364,56365],{},"Number",[299,56367,56369],{"className":21974,"code":56368,"language":21976,"meta":307,"style":307},"\u003Cinput v-model.number=\"age\" type=\"number\" />\n",[179,56370,56371],{"__ignoreMap":307},[1736,56372,56373,56375,56377,56380,56382,56385,56387,56389,56392],{"class":1738,"line":1739},[1736,56374,6657],{"class":1912},[1736,56376,43332],{"class":6696},[1736,56378,56379],{"class":2674}," v-model.number",[1736,56381,5062],{"class":1912},[1736,56383,56384],{"class":1935},"\"age\"",[1736,56386,6635],{"class":2674},[1736,56388,5062],{"class":1912},[1736,56390,56391],{"class":1935},"\"number\"",[1736,56393,6739],{"class":1912},[23,56395,56397],{"id":56396},"only-show-your-element-once","Only show your Element Once",[11,56399,56400,56403,56404,56406],{},[179,56401,56402],{},"v-once"," is used to show things only once. If you modify the input text you will not see the results change. The ",[179,56405,56402],{}," will remain showing the same text. When the page re-renders, the element/component and all its children will be treated as static content and skipped.",[299,56408,56410],{"className":21974,"code":56409,"language":21976,"meta":307,"style":307},"\u003Cspan v-once>This will never be updated: {{msg}}\u003C/span>\n",[179,56411,56412],{"__ignoreMap":307},[1736,56413,56414,56416,56418,56421,56424,56426],{"class":1738,"line":1739},[1736,56415,6657],{"class":1912},[1736,56417,1736],{"class":6696},[1736,56419,56420],{"class":2674}," v-once",[1736,56422,56423],{"class":1912},">This will never be updated: {{msg}}\u003C/",[1736,56425,1736],{"class":6696},[1736,56427,6663],{"class":1912},[23,56429,56431],{"id":56430},"v-pre-for-documenting","V-pre for Documenting",[11,56433,56434,56437],{},[179,56435,56436],{},"v-pre","will not evaluate any text that adds moustache syntax, for example, and will show it exactly as it is written.",[299,56439,56441],{"className":21974,"code":56440,"language":21976,"meta":307,"style":307},"\u003Cspan v-pre>{{ this will not be compiled }}\u003C/span>\n",[179,56442,56443],{"__ignoreMap":307},[1736,56444,56445,56447,56449,56452,56455,56457],{"class":1738,"line":1739},[1736,56446,6657],{"class":1912},[1736,56448,1736],{"class":6696},[1736,56450,56451],{"class":2674}," v-pre",[1736,56453,56454],{"class":1912},">{{ this will not be compiled }}\u003C/",[1736,56456,1736],{"class":6696},[1736,56458,6663],{"class":1912},[23,56460,56462],{"id":56461},"tag-with-data",[299,56463,56464,56465],{}," tag with ",[179,56466,56467],{},"$data",[11,56469,56470,56472,56473,56476],{},[179,56471,33399],{}," tag can be used with ",[179,56474,56475],{},"{{$data}}"," inside it and it will show you all the data you have in your data property of that file/component.",[23,56478,56480],{"id":56479},"click-and-mouse-events","Click and Mouse Events",[11,56482,56483,56486,56487,56490,56491,608,56493,891],{},[179,56484,56485],{},"v-on"," is the same as ",[179,56488,56489],{},"@"," symbol and is great for binding events like ",[179,56492,10804],{},[179,56494,56495],{},"mouseenter",[299,56497,56499],{"className":21974,"code":56498,"language":21976,"meta":307,"style":307},"\u003Cbutton v-on:click=\"counter += 1\">Add 1\u003C/button>\n",[179,56500,56501],{"__ignoreMap":307},[1736,56502,56503,56505,56507,56510,56512,56515,56518,56520],{"class":1738,"line":1739},[1736,56504,6657],{"class":1912},[1736,56506,14849],{"class":6696},[1736,56508,56509],{"class":2674}," v-on:click",[1736,56511,5062],{"class":1912},[1736,56513,56514],{"class":1935},"\"counter += 1\"",[1736,56516,56517],{"class":1912},">Add 1\u003C/",[1736,56519,14849],{"class":6696},[1736,56521,6663],{"class":1912},[11,56523,56524],{},"You can use a ternary operator inside a click event which is good for showing making sure a counter doesn't go below 0, for example.",[299,56526,56528],{"className":21974,"code":56527,"language":21976,"meta":307,"style":307},"\u003Cbutton @click=\"counter > 0 ? counter -=1 : 0\">Add 1\u003C/button>\n",[179,56529,56530],{"__ignoreMap":307},[1736,56531,56532,56534,56536,56538,56540,56543,56545,56547],{"class":1738,"line":1739},[1736,56533,6657],{"class":1912},[1736,56535,14849],{"class":6696},[1736,56537,43783],{"class":2674},[1736,56539,5062],{"class":1912},[1736,56541,56542],{"class":1935},"\"counter > 0 ? counter -=1 : 0\"",[1736,56544,56517],{"class":1912},[1736,56546,14849],{"class":6696},[1736,56548,6663],{"class":1912},[11,56550,56551,56552,608,56555,56558],{},"You can do multiple bindings which are ideal for games such as ",[179,56553,56554],{},"mouseup",[179,56556,56557],{},"mousedown"," doing different things.",[11,56560,56561,56564,56565,891],{},[179,56562,56563],{},"@mousemove.stop"," is comparable to ",[179,56566,56567],{},"e.stopPropagation()",[11,56569,56570,56573,56574,891],{},[179,56571,56572],{},"@mousemove.prevent"," is like ",[179,56575,56576],{},"e.preventDefault()",[11,56578,56579,56582],{},[179,56580,56581],{},"@submit.prevent"," will stop the page from refreshing when submitting.",[11,56584,56585,56588,56589,56591,56592,56594],{},[179,56586,56587],{},"@click.once"," this click event will be triggered once not to be confused with ",[179,56590,56402],{},". The click event will only be triggered once as opposed to ",[179,56593,56402],{}," where the text will be only rendered once. Good for when you want to stop something from submitting multiple times or something to be clicked and then you don't want the user to be able to keep on clicking, they can but it doesn't keep submitting or doing what it has probably already done",[11,56596,56597,56600],{},[179,56598,56599],{},"@click.native"," so you can listen to native events in the DOM.",[23,56602,56604],{"id":56603},"key-codes","Key Codes",[11,56606,56607],{},"You can use key codes but this is changing in vue 3 to only use names same as html spec.",[23,56609,56611],{"id":56610},"rendering-html","Rendering HTML",[11,56613,56614,56617],{},[179,56615,56616],{},"v-html"," not recommended if coming from external source.",[299,56619,56621],{"className":21974,"code":56620,"language":21976,"meta":307,"style":307},"\u003Cdiv v-html=\"html\">\u003C/div>\n",[179,56622,56623],{"__ignoreMap":307},[1736,56624,56625,56627,56629,56632,56634,56637,56639,56641],{"class":1738,"line":1739},[1736,56626,6657],{"class":1912},[1736,56628,6697],{"class":6696},[1736,56630,56631],{"class":2674}," v-html",[1736,56633,5062],{"class":1912},[1736,56635,56636],{"class":1935},"\"html\"",[1736,56638,8252],{"class":1912},[1736,56640,6697],{"class":6696},[1736,56642,6663],{"class":1912},[23,56644,56646],{"id":56645},"rendering-text","Rendering Text",[11,56648,56649,56652,56653,56655,56656],{},[179,56650,56651],{},"v-text"," is the same as using the moustache templates, ",[179,56654,30359],{},", and if you want to dynamically update then it is recommended to use moustache templates and not ",[179,56657,56651],{},[23,56659,22290],{"id":22289},[11,56661,56662,56663],{},"Example using Directives on ",[15,56664,56667],{"href":56665,"rel":56666},"https://codepen.io/debs-obrien/pen/PoNMGLJ",[19],"CodePen",[23,56669,56671,56672],{"id":56670},"unique-ids-for-v-for","Unique IDs for ",[179,56673,30901],{},[11,56675,56676,56677,56682,56683,56685],{},"Use a library to generate unique ids - ",[15,56678,56681],{"href":56679,"rel":56680},"https://www.npmjs.com/package/uuid",[19],"uuid",", then for ",[179,56684,30901],{}," you will always get a unique id.",[2011,56687,56688],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":307,"searchDepth":748,"depth":748,"links":56690},[56691,56692,56693,56694,56695,56696,56698,56699,56700,56701,56702,56703],{"id":56287,"depth":748,"text":56288},{"id":56318,"depth":748,"text":56319},{"id":56348,"depth":748,"text":56349},{"id":56396,"depth":748,"text":56397},{"id":56430,"depth":748,"text":56431},{"id":56461,"depth":748,"text":56697}," tag with $data",{"id":56479,"depth":748,"text":56480},{"id":56603,"depth":748,"text":56604},{"id":56610,"depth":748,"text":56611},{"id":56645,"depth":748,"text":56646},{"id":22289,"depth":748,"text":22290},{"id":56670,"depth":748,"text":56704},"Unique IDs for v-for","2020-10-16","Vue Directives are a great way of doing things like trimming your models or only showing something once. So many cool directives to make your life easier when coding in Vue.","photo-1503437313881-503a91226402?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=3578&q=80",{},"/blog/vue-directives",{"title":56279,"description":56706},"blog/vue-directives",[5240,5239],"f4xPGyzRzaF3KqCTUAbF5E55ya1FNSCmBWUhk98tqdU",{"id":2858,"title":2066,"body":56715,"canonical":3239,"date":3240,"description":3241,"extension":786,"featured":787,"image":788,"meta":56976,"navigation":790,"ogimage":788,"path":2065,"provider":788,"published":790,"seo":56977,"stem":3244,"tags":56978,"url":788,"__hash__":3246},{"type":8,"value":56716,"toc":56968},[56717,56719,56721,56723,56725,56735,56743,56745,56747,56749,56753,56757,56771,56776,56780,56786,56791,56795,56799,56804,56806,56820,56824,56828,56830,56835,56837,56839,56843,56847,56851,56857,56863,56867,56869,56871,56873,56875,56879,56883,56887,56889,56891,56893,56903,56905,56909,56914,56918,56923,56934,56936,56941,56943,56948,56952,56954,56959,56961,56966],[11,56718,2863],{},[11,56720,2866],{},[138,56722,2870],{"id":2869},[11,56724,2873],{},[70,56726,56727,56731],{},[73,56728,56729,2881],{},[58,56730,2880],{},[73,56732,56733,2887],{},[58,56734,2886],{},[11,56736,2890,56737,2894,56739,2897,56741,2900],{},[58,56738,2893],{},[179,56740,2140],{},[179,56742,2140],{},[121,56744],{"src":2903,"alt":2904},[11,56746,2907],{},[138,56748,2911],{"id":2910},[11,56750,2914,56751,2918],{},[179,56752,2917],{},[11,56754,56755],{},[58,56756,2923],{},[11,56758,2926,56759,829,56761,2933,56763,2937,56765,2941,56767,2944,56769,2947],{},[179,56760,2929],{},[179,56762,2932],{},[179,56764,2936],{},[179,56766,2940],{},[179,56768,60],{},[179,56770,2917],{},[299,56772,56774],{"className":56773,"code":2951,"language":304},[302],[179,56775,2951],{"__ignoreMap":307},[11,56777,56778],{},[58,56779,2958],{},[11,56781,2961,56782,2964,56784,2967],{},[179,56783,2917],{},[179,56785,2140],{},[299,56787,56789],{"className":56788,"code":2971,"language":304},[302],[179,56790,2971],{"__ignoreMap":307},[11,56792,56793],{},[58,56794,2978],{},[11,56796,2981,56797,2984],{},[179,56798,2140],{},[299,56800,56802],{"className":56801,"code":2988,"language":304},[302],[179,56803,2988],{"__ignoreMap":307},[11,56805,2993],{},[70,56807,56808,56816],{},[73,56809,56810,3001,56812,3004,56814,3007],{},[58,56811,3000],{},[179,56813,2917],{},[179,56815,2917],{},[73,56817,56818,3013],{},[58,56819,3012],{},[11,56821,56822],{},[58,56823,3018],{},[11,56825,3021,56826,3025],{},[58,56827,3024],{},[11,56829,3028],{},[299,56831,56833],{"className":56832,"code":3032,"language":304},[302],[179,56834,3032],{"__ignoreMap":307},[11,56836,3037],{},[138,56838,3041],{"id":3040},[11,56840,3044,56841,3048],{},[58,56842,3047],{},[1713,56844,56845],{},[11,56846,3053],{},[11,56848,3056,56849,3059],{},[179,56850,2140],{},[11,56852,56853,3065,56855],{},[58,56854,3064],{},[133,56856,3068],{},[11,56858,56859,3074,56861],{},[58,56860,3073],{},[133,56862,3077],{},[11,56864,3080,56865,3083],{},[179,56866,2140],{},[11,56868,3086],{},[138,56870,3090],{"id":3089},[11,56872,3093],{},[121,56874],{"src":3096,"alt":3097},[11,56876,56877,3103],{},[58,56878,3102],{},[11,56880,56881,3109],{},[58,56882,3108],{},[11,56884,56885,3115],{},[58,56886,3114],{},[11,56888,3118],{},[138,56890,3122],{"id":3121},[11,56892,3125],{},[70,56894,56895,56899],{},[73,56896,56897,3133],{},[58,56898,3132],{},[73,56900,56901,3139],{},[58,56902,3138],{},[11,56904,3142],{},[11,56906,56907,1087],{},[58,56908,3147],{},[299,56910,56912],{"className":56911,"code":3151,"language":304},[302],[179,56913,3151],{"__ignoreMap":307},[11,56915,56916,1087],{},[58,56917,3158],{},[299,56919,56921],{"className":56920,"code":3162,"language":304},[302],[179,56922,3162],{"__ignoreMap":307},[11,56924,2506,56925,3169,56927,3175,56930,3178,56932,891],{},[179,56926,2940],{},[15,56928,3174],{"href":3172,"rel":56929},[19],[179,56931,2936],{},[179,56933,2929],{},[138,56935,3184],{"id":3183},[11,56937,3187,56938,3193],{},[15,56939,3192],{"href":3190,"rel":56940},[19],[11,56942,3196],{},[299,56944,56946],{"className":56945,"code":3200,"language":304},[302],[179,56947,3200],{"__ignoreMap":307},[11,56949,3205,56950,3209],{},[179,56951,3208],{},[11,56953,3212],{},[299,56955,56957],{"className":56956,"code":3216,"language":304},[302],[179,56958,3216],{"__ignoreMap":307},[11,56960,3221],{},[299,56962,56964],{"className":56963,"code":3225,"language":304},[302],[179,56965,3225],{"__ignoreMap":307},[11,56967,3230],{},{"title":307,"searchDepth":748,"depth":748,"links":56969},[56970,56971,56972,56973,56974,56975],{"id":2869,"depth":756,"text":2870},{"id":2910,"depth":756,"text":2911},{"id":3040,"depth":756,"text":3041},{"id":3089,"depth":756,"text":3090},{"id":3121,"depth":756,"text":3122},{"id":3183,"depth":756,"text":3184},{},{"title":2066,"description":3241},[795,796],{"id":56980,"title":56981,"body":56982,"canonical":788,"date":56986,"description":56987,"extension":786,"featured":787,"image":56988,"meta":56989,"navigation":790,"ogimage":788,"path":56990,"provider":3460,"published":787,"seo":56991,"stem":56992,"tags":56993,"url":56994,"__hash__":56995},"blog/blog/whats-it-like-to-work-after-studying-online.md","What It’s Like To Work After Studying Online",{"type":8,"value":56983,"toc":56984},[],{"title":307,"searchDepth":748,"depth":748,"links":56985},[],"2017-11-30","I was one of the first graduates from the Front-End Web Development course. I had played around with different online courses in coding before, but really found what I was looking for with OpenClassrooms. Fast forward 8 months, I had completed all projects and managed to get myself a job at Logitravel, a holiday booking site based in Palma, Mallorca.","v1607269937/debbie.codes/OC_DEbbie_tny82j",{"platform":33010},"/blog/whats-it-like-to-work-after-studying-online",{"title":56981,"description":56987},"blog/whats-it-like-to-work-after-studying-online",[3464],"https://blog.openclassrooms.com/en/2017/11/30/like-works-study-online/","A-J5L78WQigghXUwgs1TwpXr5a__xCZ9Hkkg2RxiTrQ",{"id":56997,"title":56998,"body":56999,"canonical":788,"date":57153,"description":57154,"extension":786,"featured":787,"image":57155,"meta":57156,"navigation":790,"ogimage":788,"path":57157,"provider":3460,"published":787,"seo":57158,"stem":57159,"tags":57160,"url":788,"__hash__":57161},"blog/blog/why-i-love-nuxt.md","Why I Love Nuxt",{"type":8,"value":57000,"toc":57142},[57001,57004,57007,57011,57014,57017,57021,57024,57028,57031,57035,57038,57041,57045,57048,57052,57055,57058,57062,57065,57068,57072,57075,57078,57081,57094,57100,57102],[11,57002,57003],{},"I was researching frameworks in a previous Job and I was having server side rendering issues as SPA was something that no company wanted. Vue was great but just didn’t fit for us and I was recommended by a Vue Core Team member to use Nuxt. So we gave it a try. And it just worked. Nuxt was quite young at the time and after creating a prototype in record time the company dismissed the idea of using Nuxt as they were too afraid of something so new. Or perhaps they were too afraid of something so good.",[11,57005,57006],{},"when I moved to another company I told them I wanted to use Nuxt. That I really believed in the framework and that it was the right tool at least for the majority of cases. They told me as Tech Lead I could decide what tech to use but that some clients already had projects set up in Angular and others I would need to convince. Luckily I like a challenge.",[23,57008,57010],{"id":57009},"teaching-the-team","Teaching the Team",[11,57012,57013],{},"However my team didn’t know Nuxt or Vue for that matter. So I now had to convince my team it was a good choice and of course teach them. Luckily Nuxt is really easy to learn so it took very little time to teach them. The only thing was that I was no expert at Nuxt and all of a sudden I was leading a team and projects. There wasn’t many resources out there for learning Nuxt and I was faced with many challenges that I just didn’t know how to solve.",[11,57015,57016],{},"Luckily the Nuxt team were really helpful and answered all my questions and I even hired Alex Lichter, one of the Nuxt maintainers to help us out. That’s when I really learnt Nuxt and it helped a lot. Although my learning was at the expense of the company they also gained as my knowledge was then passed down to the other team members. We started out with in house projects and when you lead a team and they do all the work and are able to deliver a top performant site with ease then you know you are using the right tool.",[23,57018,57020],{"id":57019},"time-to-convince-the-clients","Time to Convince the Clients",[11,57022,57023],{},"I tried to convince a massive hotel chain to use Nuxt and gave a great pitch but they went with AEM, Adobe Experience Manager, which is a more complete package in terms of CMS for non tech people and for them the thoughts of using a headless CMS was just a crazy idea. Actually they told me I was too far ahead in tech. But I still got to lead that project for the frontend making sure it was as performant as possible and only allowing certain libraries, ensuring to best practices etc.",[23,57025,57027],{"id":57026},"you-want-static-i-can-give-you-static","You want Static? I can give you Static",[11,57029,57030],{},"I found it hard to convince clients and it was so frustrating as I knew Nuxt was a good choice for them. For one project we were asked to create a static HTML site and I told my boss that this was the project. We would deliver a Nuxt static site. They would get all the performance benefits and an easy to maintain site. We just don’t tell them we are using modern technology. For some reason that scares people and I had no intentions of delivering a HTML site with no framework in 2019. No way. So I told my boss we just deliver it. And so we did.",[23,57032,57034],{"id":57033},"oh-to-be-a-devops-expert","Oh to be a DevOps expert",[11,57036,57037],{},"But we had so many deployment issues and the company were not happy. They asked for a HTML site and got a Nuxt site. Now as a Frontend Tech Lead I had to become a DevOps expert and find a way to deploy the Nuxt site. It wasn’t easy and I remember reaching out to Sébastien Chopin and asking for help while in the clients office with managers panicking as they were going live and the site wasn't working.",[11,57039,57040],{},"They were deploying on NGINX and I knew nothing about how that worked. We finally managed to get it to work after changing servers and things way beyond my knowledge. It was touch and go for a while and everything was landing on my head. But once they got it to work and saw how fast it was and how easy it was to add more pages, more content and more languages, they were in fact really happy. And from there it was Nuxt sites from that day forward with no questions asked.",[23,57042,57044],{"id":57043},"things-are-never-easy","Things are never easy",[11,57046,57047],{},"But not everyone has a server or needs to have a server to work with Nuxt so I need to persuade backend developers that static sites were the future. Going Jamstack, the new cool trendy word. I ran into a lot of issues as people who are used to server rendered sites just compare static sites with static sites of the 90's. I think that is why I created so many talks on static sites because I really needed to know my stuff so I could convince others on my team. Of course they had so many doubts about how Jamstack works, how Nuxt works, and it was really hard trying to get my point across.",[23,57049,57051],{"id":57050},"there-are-always-doubts","There are always doubts",[11,57053,57054],{},"I constantly got the question of \"Are you trying to tell me that if I change some content I have to rebuild and deploy the site?\". And the answer if of course yes which makes people think that static is not for them. But if you set things up correctly so it builds on each push to git or on a webhook from the CMS then really what is the problem. Oh you have to wait perhaps 2 minutes to see the changes. Ok yeah, but come on, the performance benefits are so much worth it plus its free to host it and much more secure and easier than working with servers.",[11,57056,57057],{},"I am not very good at backing down and I intended not to lose this battle. I was convinced that a Nuxt static site was the perfect case for an agency site, a travel agent site, and hotel promotion sites. The funny thing is that the team fought against static because you had to rebuild the site and it actually made no sense as content only got updated once every few weeks, if even that.",[23,57059,57061],{"id":57060},"understanding-static-sites","Understanding static sites",[11,57063,57064],{},"Many people still don't understand static sites and all their power. We built a travel agent site with a booking engine where the site was static meaning it was lightening fast in performance but we excluded the booking engine from static generation and therefore it fell back to being an SPA. This meant these pages had no SEO but really they didn't need it. They were just search results pages. We managed to do the unthinkable. Or perhaps it was me just trying to prove a point in that we didn't need server side rendering and that with Jamstack you really can do anything.",[11,57066,57067],{},"My boss at the time said \"But can we not just render the content and not the whole site if we only change content?\". He was right in that, yes that would be lovely but at the time it just wasn't available. Maybe in the future. Sometimes you have to take a bet on things. Things are never perfect but they are always improving and things will always get better. And Nuxt delivered this exact thing where your site gets rebuilt and doesn't go through webpack if only your content changes from either your CMS or when using content module for example. And this makes rebuilding and deploying your site super fast. But",[23,57069,57071],{"id":57070},"nuxt-is-just-amazing","Nuxt is just amazing",[11,57073,57074],{},"For me Nuxt just works. It is easy to learn and easy to teach people. I think anyone can pick it up easily and get a site up and running with not much dev experience and that is super cool. With Nuxt you can have non tech people such as designers helping out on a project as the code base is just so easy to understand.",[11,57076,57077],{},"When you are in charge of a project and are not sure where the deployment might be or if you are just not sure if static is right for you then no worries as with Nuxt just changing one thing in the config file and your site becomes static or server rendered and that is super powerful.",[11,57079,57080],{},"But Nuxt also lets you take it the the next level by creating your own plugins and modules and literally hooking into Nuxt. So basically if you want my opinion, is Nuxt right for you? For sure it is. For your, for your company, for your team.",[11,57082,57083,57084,57088,57089,57093],{},"If you want to get started with Nuxt you can check out my ",[15,57085,57087],{"href":3762,"rel":57086},[19],"free course on getting Started with Nuxt"," or check out my ",[15,57090,57092],{"href":3630,"rel":57091},[19],"YouTube Channel"," for free content on Nuxt. Have fun.",[11,57095,57096],{},[121,57097],{"alt":57098,"src":57099},"Are you Nuxt","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1599991772/debbie.codes/vue_amsterdam_2_vkc631.jpg",[23,57101,5706],{"id":5705},[70,57103,57104,57110,57117,57124,57131,57136],{},[73,57105,57106],{},[15,57107,57109],{"href":3730,"rel":57108},[19],"Previous conference Talks",[73,57111,57112],{},[15,57113,57116],{"href":57114,"rel":57115},"https://dev.to/azure/why-static-sites-are-back-6jh",[19],"Why static sites are back",[73,57118,57119],{},[15,57120,57123],{"href":57121,"rel":57122},"https://nuxtjs.org/blog/nuxt-static-improvements",[19],"Nuxt Static Improvements",[73,57125,57126],{},[15,57127,57130],{"href":57128,"rel":57129},"https://nuxtjs.org/blog/going-full-static",[19],"Going Full Static",[73,57132,57133],{},[15,57134,21108],{"href":3630,"rel":57135},[19],[73,57137,57138],{},[15,57139,57141],{"href":3762,"rel":57140},[19],"Free course on Getting started with Nuxt",{"title":307,"searchDepth":748,"depth":748,"links":57143},[57144,57145,57146,57147,57148,57149,57150,57151,57152],{"id":57009,"depth":748,"text":57010},{"id":57019,"depth":748,"text":57020},{"id":57026,"depth":748,"text":57027},{"id":57033,"depth":748,"text":57034},{"id":57043,"depth":748,"text":57044},{"id":57050,"depth":748,"text":57051},{"id":57060,"depth":748,"text":57061},{"id":57070,"depth":748,"text":57071},{"id":5705,"depth":748,"text":5706},"2021-01-30","I have been using Nuxt in all of my recent tech jobs but why did I start using it? What problems did I have trying to convince the team and more important the clients. And why should you use Nuxt?","f_auto,q_auto/v1599991772/debbie.codes/vue_amsterdam_2_vkc631",{},"/blog/why-i-love-nuxt",{"title":56998,"description":57154},"blog/why-i-love-nuxt",[5239,3464],"e8wulik77_4FE2YcfiSDxCzrJ3GUoyMaNYSPa93YyN4",{"id":57163,"title":57164,"body":57165,"canonical":788,"date":57263,"description":57264,"extension":786,"featured":787,"image":57265,"meta":57266,"navigation":790,"ogimage":788,"path":57267,"provider":3460,"published":787,"seo":57268,"stem":57269,"tags":57270,"url":788,"__hash__":57271},"blog/blog/why-livestreams-scare-me.md","Why Live Streams Scare me",{"type":8,"value":57166,"toc":57253},[57167,57170,57174,57177,57181,57199,57203,57206,57210,57213,57216,57220,57223,57226,57230,57233,57237,57240,57244],[11,57168,57169],{},"If any of you have watched my Live Streams you might think \"Wow, she has so much energy and is so confident. I could never do what she does\". But if you only knew. You see for me live streams are exciting. I love them but they scare the hell out of me. It is where you expose yourself to the world and literally say, \"this is me and this is how I do things\". No editing no nothing. People live just watching over your shoulders and it is terrifying.",[23,57171,57173],{"id":57172},"so-why-do-i-do-it","So why do I do it?",[11,57175,57176],{},"Because I love it. I love connecting with people and I love sharing my knowledge with the world. Just sometimes I really feel that I do not have enough to share. That the wold already nows what I know and that I am not going to teach them anything new. Yes, Imposter Syndrome is something I deal with every day. I also am aware that I do not know everything and I am not the best developer in the world. I have so much more to learn and improve.",[23,57178,57180],{"id":57179},"there-are-always-choices","There are always choices",[11,57182,57183,57184,829,57189,608,57194,891],{},"But I can do two things. I can either just give up and say \"What's the point in me doing live streams if other people can do it better!\". Or I can just keep doing it and every time I do it I will get better and better and before I know it I will be as confident and comfortable as ",[15,57185,57188],{"href":57186,"rel":57187},"https://www.learnwithjason.dev/",[19],"Jason Lengstorf",[15,57190,57193],{"href":57191,"rel":57192},"https://www.jamesqquick.com/",[19],"James Quick",[15,57195,57198],{"href":57196,"rel":57197},"https://www.youtube.com/channel/UC5mnBodB73bR88fLXHSfzYA",[19],"Eddie Jaroude",[23,57200,57202],{"id":57201},"why-do-we-compare-ourselves","Why do we compare ourselves?",[11,57204,57205],{},"We always compare ourselves to others in such a way that it's just not good. I mean it would be like someone in football comparing themselves to Messi. I mean most people want to be Messi but it would be impossible to compare yourself to him. The same goes for Live Streaming. Jason Lengstorf has been doing it for years and for sure his first streams were not like his streams of today. And we all have to take that into account. Of course that doesn't help when you are the one about to go out and do the live stream. The world is watching and the pressure is one.",[23,57207,57209],{"id":57208},"people-are-actually-nice","People are actually nice",[11,57211,57212],{},"However the world is not such a bad place after all. At least I have been lucky in that the people who come to watch are good people who are just trying to learn more and are really very supportive and understand you are giving your time to them so they can learn with no cost at all. Sometimes we forget that and try to deliver a perfect product and perfection isn't normal so we shouldn't try to be it.",[11,57214,57215],{},"I personally love when I go live and people put likes in the chat and say Hi. I read every single message and see every single like and it gives me so much more energy.",[23,57217,57219],{"id":57218},"other-people-feel-the-same","Other People feel the same",[11,57221,57222],{},"Today I had a great chat with a friend of mine who also told me he is afraid of doing Live Streams. It really is incredible how people you admire are also suffering from the same thing as you. They also fear they are not good enough. Imagine if we all felt that and then we all just didn't do live streams or share knowledge. The virtual world for sure would be a very sad place.",[11,57224,57225],{},"We are all in this together, we are all still learning and improving. The only way to grow is to do the things that make us feel uncomfortable. I want to improve my coding so much. I know I am not the best and I have so much to learn. Learning in front of people is scary as hell but I am going to keep doing it. I mean what is the worst that can happen? If things go really bad I could always just pretend to lose internet. Ohhh and now you know my secret for if I fail. But you see the more we fail at things the more we improve and the more we get things wrong the more we grow and succeed.",[23,57227,57229],{"id":57228},"my-goals","My goals",[11,57231,57232],{},"My goal is to improve at live streams. My goal this year is to be a better me. To share more with people so they can perhaps have a better life, get a better job or just feel better about themselves. I was lucky to have an amazing mentor who helped me on my coding journey and many people who constantly helped me out when I was stuck on coding challenges. To give back to the community costs nothing and if we all just help one person a day the world really will be a better place.",[23,57234,57236],{"id":57235},"just-do-it","Just do it",[11,57238,57239],{},"And if you are thinking that you want to do something but are afraid then don't be. Just do it. We all feel the same. And Actually we made a new rule today. If you say you will do things 3 times then either do it or stop putting on your list of things to do. Taking the step is always hard but if you don't take it you will never grow.",[23,57241,57243],{"id":57242},"follow-me","Follow me",[11,57245,57246,57247,57252],{},"Feel free to ",[15,57248,57251],{"href":57249,"rel":57250},"https://www.youtube.com/c/DebbieOBrien",[19],"follow me on YouTube"," and make sure you say hi when I am live. All messages and emojis are very much appreciated. I still haven't figured out how to schedule or program things. Am still just figuring things out but one day I will look back at this post and appreciate how far I have come.",{"title":307,"searchDepth":748,"depth":748,"links":57254},[57255,57256,57257,57258,57259,57260,57261,57262],{"id":57172,"depth":748,"text":57173},{"id":57179,"depth":748,"text":57180},{"id":57201,"depth":748,"text":57202},{"id":57208,"depth":748,"text":57209},{"id":57218,"depth":748,"text":57219},{"id":57228,"depth":748,"text":57229},{"id":57235,"depth":748,"text":57236},{"id":57242,"depth":748,"text":57243},"2021-02-05","What its like to do a live stream, why it scares me and why I know the only way to get over fear is to keep doing it again and again.","fl_lossy,f_auto/v1609438516/debbie.codes/blog/livestreams_mg6ulm",{},"/blog/why-livestreams-scare-me",{"title":57164,"description":57264},"blog/why-livestreams-scare-me",[3464],"2ucFUO10TMSRAS1LGt1Ap-jIN_b862PFjb_xJ96G4VU",{"id":57273,"title":57274,"body":57275,"canonical":788,"date":28456,"description":57560,"extension":786,"featured":787,"image":57561,"meta":57562,"navigation":790,"ogimage":788,"path":4739,"provider":3460,"published":790,"seo":57564,"stem":57565,"tags":57566,"url":788,"__hash__":57567},"blog/blog/why-microsoft.md","Why Microsoft and How I achieved my Goal",{"type":8,"value":57276,"toc":57537},[57277,57281,57284,57287,57291,57294,57297,57303,57307,57310,57313,57319,57323,57326,57329,57334,57340,57343,57347,57350,57355,57358,57362,57365,57369,57372,57378,57382,57385,57390,57393,57399,57403,57406,57410,57413,57419,57423,57426,57430,57437,57441,57448,57452,57464,57468,57481,57485,57497,57501,57508,57512,57515,57518,57522,57525,57530,57534],[23,57278,57280],{"id":57279},"i-started-building-websites-in-1997","I started building websites in 1997",[11,57282,57283],{},"I started building websites in 1997 before I even had access to the internet. I got my first job in tech in 1999 working with Dreamweaver. But it was very boring so I left after a year to have fun and be an entertainer on a stage in Mallorca. I had fun for a few years but then started studying again to try to get back into tech starting with flash and then moving on to wordpress.",[11,57285,57286],{},"After building many websites for the local bars, restaurants, fish mongers and Taekwondo clubs, I finally got hired and started working for startups as a front-end developer specializing in HTML and CSS. I was let go many times as each startup kept running out of money and so I was constantly in and out of work. It was very hard to survive, so I became an English teacher to be able to pay the bills and did web work when I could.",[23,57288,57290],{"id":57289},"my-dream-to-work-for-a-cool-tech-company","My Dream - To work for a Cool Tech Company",[11,57292,57293],{},"It all changed 5 years ago when my life was not really going anywhere and I was pretty miserable. That's when I stared to do a dream builders course so I could change my life. My dream was to work for Google as a frontend developer. Wait a second you say, I thought this was titled \"Why Microsoft?\".",[11,57295,57296],{},"Let me explain. At that time in my life Microsoft was not my dream. It wasn't the company that I know it to be today, so it wasn't my goal. During my dream builders course I was told to define Google. For me it was never about the cool offices or free food or whatever else makes Google cool. It was about working with a great team of people, it was about being able to make a difference, it was about always being able to learn and improve, and I won't lie in saying it was also about working for a company that wasn't going to close down after a few years.",[11,57298,57299],{},[121,57300],{"alt":57301,"src":57302},"me writing my dream in a notebook","https://res.cloudinary.com/debsobrien/image/upload/v1651580853/debbie.codes/blog/2022/CleanShot_2022-05-03_at_14.27.03_2x_wxvvt5.png",[23,57304,57306],{"id":57305},"becoming-microsoft-certified","Becoming Microsoft Certified",[11,57308,57309],{},"After leaving my teaching job and freelance dev work I went back to full time study in February 2017, to learn JavaScript, in the hope to get a job at a great company. I never thought I would ever get hired by Google but I always feel you have to reach for the stars and aim as high as possible. After about 9 months of full time studying I got a job as a real frontend developer in Bluekiri in November of 2017.",[11,57311,57312],{},"This is when things changed. This company were Microsoft Partners and the CEO encouraged everyone to write blog posts, speak at conferences, collaborate on Open Source and be Microsoft certified, an achievement that was not easy, but that I am very proud of.",[11,57314,57315],{},[121,57316],{"alt":57317,"src":57318},"microsoft exam certification","https://res.cloudinary.com/debsobrien/image/upload/v1651560634/debbie.codes/blog/2022/microsft-certified_2x_a5vmkd.png",[23,57320,57322],{"id":57321},"building-chat-bots-with-the-microsoft-guys","Building Chat Bots with the Microsoft Guys",[11,57324,57325],{},"While working there we were tasked to build chat bots in Node.js. I in no way had close to enough experience, but was allowed to shadow the project and build some of the UI for the chat bots dashboard. Microsoft sent us 2 people to help us build these chat bots. The developers sat with us and programmed the chat bots with us and helped us better understand the product we were using. It was so much fun.",[11,57327,57328],{},"We had lunch with these guys every day and on the last day I shared a taxi with them to the airport and I remember sitting in the airport with them with my book on prepping to be Microsoft certified, and they told me what it was like to work for Microsoft. And that is when my goals changed. I just really liked the idea that Microsoft were willing to send people out to help others, to help them build better software with their products and I said to the two Microsoft guys,",[1713,57330,57331],{},[11,57332,57333],{},"\"One day I will work for Microsoft\".",[11,57335,57336],{},[121,57337],{"alt":57338,"src":57339},"a group of developers building looking at a screen of code","https://res.cloudinary.com/debsobrien/image/upload/v1651562104/debbie.codes/blog/2022/building-chatbots-at-bluekiri_jyd3a5.jpg",[11,57341,57342],{},"But again it was just a goal, a dream. How does one ever even get there? As always, it takes time. A lot of time. At that moment I was no way good enough to work for Microsoft. I needed a lot more experience and skills.",[23,57344,57346],{"id":57345},"what-are-your-goals","What are your goals?",[11,57348,57349],{},"After a year working for Bluekiri I moved to an agency. During my interview in October 2018, I was asked the typical question. Where do you see yourself in 5 years time? What are your goals? My answer was simple. I said:",[1713,57351,57352],{},[11,57353,57354],{},"I will be working for Microsoft.",[11,57356,57357],{},"Now picture it for a second. A female developer who learnt JavaScript a year ago, is telling an interview panel in a small agency, in the small island of Mallorca, that she is going to work for Microsoft and says it with such determination and belief. Well their jaws dropped and their eyes opened wide. I don't think either I or them thought it would happen but I had a goal and my goal was very clear.",[23,57359,57361],{"id":57360},"the-mvp-program","The MVP Program",[11,57363,57364],{},"In November 2018 I spoke at my first conference and got to meet speakers who were Microsoft Most Valuable Professionals, and they started telling me about the MVP program. This became part of my goal. What did I need to do? How could I become one? I was already contributing to open source, helping communities and sharing knowledge so really it was just about doing more of it and trying to help people even more.",[138,57366,57368],{"id":57367},"visiting-the-microsoft-office-in-boston","Visiting the Microsoft Office in Boston",[11,57370,57371],{},"While on holiday in Boston, in November 2019, I went to the Microsoft offices to help Jen Looper mentor a group of women who were learning to code. I literally told my husband to go shopping and meet me later. For me it was just a dream walking into the offices and seeing the Microsoft logo. I felt like I was taking a step closer to my dream. And after that trip is when Jen Looper nominated me for the MVP program.",[11,57373,57374],{},[121,57375],{"alt":57376,"src":57377},"me sitting at at table with Microsoft logo on the table cloth","https://res.cloudinary.com/debsobrien/image/upload/v1651559990/debbie.codes/blog/2022/microsoft-offices-boston_vhzguu",[138,57379,57381],{"id":57380},"my-mvp-award","My MVP Award",[11,57383,57384],{},"On the 1st January 2020 I received the email to say I had been accepted into the program and I was blown away. I really couldn't believe it. I had also recently been awarded the Google Developer Expert award and so to be acknowledged by two of the most amazing tech companies, two companies that it would be a dream to work for, was just insane.",[11,57386,57387],{},[121,57388],{"alt":57389,"src":4038},"me with my mvp awards",[11,57391,57392],{},"In January 2020 I got to travel on a cruise ship for 17 days with a bunch of speakers and MVPs to give a tech talk in the Antarctic waters. It was such an incredible experience to be surrounded by such amazing speakers and MVPs. I grew so much during that cruise and it really helped me believe even more that I could accomplish my goals.",[11,57394,57395],{},[121,57396],{"alt":57397,"src":57398},"a bunch of people the stairs making silly faces and all wearing warm hats","https://res.cloudinary.com/debsobrien/image/upload/v1599990373/debbie.codes/artctica_speakers_kvfixk.jpg",[138,57400,57402],{"id":57401},"first-mvp-summit","First MVP Summit",[11,57404,57405],{},"In March 2020 it was my first MVP summit, which I was meant to attend in person, but at last minute it was moved to be remote due to Covid, but I still really enjoyed it. I really felt I was part of Microsoft in a way. I loved the sessions and the hallway tracks were amazing with many of us MVPs just chatting and hanging out and supporting each other in these difficult times.",[23,57407,57409],{"id":57408},"becoming-a-github-star","Becoming a GitHub Star",[11,57411,57412],{},"In April 2020 I started working for Nuxt, which was just incredible, but also my first job as a Developer Advocate in Covid times made things rather different. However I used my time to focus on my goals, to do everything I could to just be better at what I did, and I shared as much as I could with others. And that's how later that year I became a GitHub Star. My belief is always, if I could change my life then others can too. They just have to see and believe that they can. So I share my knowledge and learnings with everyone so they can make their dreams come true.",[11,57414,57415],{},[121,57416],{"alt":57417,"src":57418},"me with my GitHub swag and awards behind me","https://res.cloudinary.com/debsobrien/image/upload/v1619366231/debbie.codes/blog/IMG_8224_pe76ma.heic",[23,57420,57422],{"id":57421},"collaborations-as-an-mvp","Collaborations as an MVP",[11,57424,57425],{},"As an MVP I got to collaborate a lot with many people from Microsoft which was just amazing. Special thanks to Simona Cotin and Wassim Chegham for always believing in me and for giving me these amazing opportunities.",[138,57427,57429],{"id":57428},"microsoft-build-2020","Microsoft Build 2020",[11,57431,57432,57433],{},"I got to appear on Microsoft Build in 2020 alongside Simona Cotin and others for a short interview on Nuxt and static sites which was an incredible experience. ",[121,57434],{"alt":57435,"src":57436},"me appearing on Microsoft Build","https://res.cloudinary.com/debsobrien/image/upload/v1651592831/debbie.codes/blog/2022/buildDebbie_c7q789.jpg",[138,57438,57440],{"id":57439},"nuxt-and-azure-static-web-apps-ms-build","Nuxt and Azure Static Web Apps MS Build",[11,57442,57443,57444],{},"I got to collaborate with Simona Cotin on testing the Nuxt integration and docs for Azure Static Web Apps and had my demo shown live on MS Build. ",[121,57445],{"alt":57446,"src":57447},"nuxtjs site on azure static web apps","https://res.cloudinary.com/debsobrien/image/upload/v1651587315/debbie.codes/blog/2022/nuxt-azure-msbuild_idmysz.jpg",[138,57449,57451],{"id":57450},"ms-create","MS Create",[11,57453,57454,57455,57459,57460],{},"I appeared on MS Create with Wassim Chegham and Burke Holland talking about ",[15,57456,57458],{"href":57114,"rel":57457},[19],"why static sites were back, from static, servers browsers to static",". ",[121,57461],{"alt":57462,"src":57463},"me appearing on MS Create with Wassim","https://res.cloudinary.com/debsobrien/image/upload/v1651585540/debbie.codes/blog/2022/EeG0c_GWoAADYpO_oxgjhu.jpg",[138,57465,57467],{"id":57466},"one-dev-question","One Dev Question",[11,57469,57470,57471,57476,57477],{},"I created a mini Twitter video series for ",[15,57472,57475],{"href":57473,"rel":57474},"https://twitter.com/i/events/1364046892556976129?s=20",[19],"Microsoft's One Dev Question on Nuxt.js",". All recorded remotely on the many reasons to use Nuxt and Azure Static Web Apps. ",[121,57478],{"alt":57479,"src":57480},"me at home with mvp award in background and microsoft logo on screen","https://res.cloudinary.com/debsobrien/image/upload/v1651559496/debbie.codes/blog/2022/microsoft-one-dev-question_2x_ljwwp1.png",[138,57482,57484],{"id":57483},"microsoft-startup-developers","Microsoft Startup Developers",[11,57486,57487,57488,57459,57493],{},"I had an article published on the Microsoft Startup Developers blog on ",[15,57489,57492],{"href":57490,"rel":57491},"https://devblogs.microsoft.com/startups/nuxt/",[19],"how we use Nuxt at the Nuxt.js Company",[121,57494],{"alt":57495,"src":57496},"article on how we use nuxt","https://res.cloudinary.com/debsobrien/image/upload/v1651585852/debbie.codes/blog/2022/Article-nuxt-microsoft_2x_jqgltv.png",[138,57498,57500],{"id":57499},"microsoft-cloud-skills","Microsoft Cloud Skills",[11,57502,57503,57504],{},"I also appeared on the Microsoft Cloud Skills show on JavaScript Communities where we got to talk about an intro to JavaScript Frameworks, with Wassim Chegham, Yohan Lasorsa and Ana Cidre. ",[121,57505],{"alt":57506,"src":57507},"me and the others back stage for the show","https://res.cloudinary.com/debsobrien/image/upload/v1651586279/debbie.codes/blog/2022/cloud-skills-show_hijswh.png",[23,57509,57511],{"id":57510},"having-clear-goals","Having Clear Goals",[11,57513,57514],{},"These were all my tiny steps to one day achieve my goal, but also these opportunities made me realize even more what a cool company Microsoft is.",[11,57516,57517],{},"In February 2021 I interviewed for Bit and the CEO asked me what my 3 year plan was, what my goal was. And I told him, again with extreme confidence,",[1713,57519,57520],{},[11,57521,57354],{},[11,57523,57524],{},"And when I handed in my notice to him, I asked him if he remembered my answer and he said, \"yes, Microsoft\".",[11,57526,57527,57528,891],{},"So how did it happen? I guess it was right time and right place. A little bit of luck but also being prepared. Having a goal in mind and just going for it until the right opportunity arose. You can read all about it in my post ",[15,57529,28025],{"href":28461},[23,57531,57533],{"id":57532},"my-dream-came-true","My dream came true",[11,57535,57536],{},"I still can't quite believe I actually got the job. I'm still on a high and floating in the clouds. 5 years a go I used all my life savings to study and learn JavaScript and now at 43 years of age (yes I know you all think I am only 21) I get to work for Microsoft. It really is just incredible what one can achieve.",{"title":307,"searchDepth":748,"depth":748,"links":57538},[57539,57540,57541,57542,57543,57544,57549,57550,57558,57559],{"id":57279,"depth":748,"text":57280},{"id":57289,"depth":748,"text":57290},{"id":57305,"depth":748,"text":57306},{"id":57321,"depth":748,"text":57322},{"id":57345,"depth":748,"text":57346},{"id":57360,"depth":748,"text":57361,"children":57545},[57546,57547,57548],{"id":57367,"depth":756,"text":57368},{"id":57380,"depth":756,"text":57381},{"id":57401,"depth":756,"text":57402},{"id":57408,"depth":748,"text":57409},{"id":57421,"depth":748,"text":57422,"children":57551},[57552,57553,57554,57555,57556,57557],{"id":57428,"depth":756,"text":57429},{"id":57439,"depth":756,"text":57440},{"id":57450,"depth":756,"text":57451},{"id":57466,"depth":756,"text":57467},{"id":57483,"depth":756,"text":57484},{"id":57499,"depth":756,"text":57500},{"id":57510,"depth":748,"text":57511},{"id":57532,"depth":748,"text":57533},"I just really liked the idea that Microsoft were willing to send people out to help others, to help them build better software with their products and I said to the two Microsoft guys, \"One day I will work for Microsoft\".","v1651559990/debbie.codes/blog/2022/microsoft-offices-boston_vhzguu",{"ogImage":57563},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1651559990/debbie.codes/blog/2022/microsoft-offices-boston_vhzguu",{"title":57274,"description":57560},"blog/why-microsoft",[3464],"uIQOGilqT5NcMgbnp9MuxxaGke9ZYMf7zP46jm9NIVA",{"id":57569,"title":57116,"body":57570,"canonical":788,"date":20484,"description":57574,"extension":786,"featured":787,"image":57575,"meta":57576,"navigation":790,"ogimage":788,"path":57578,"provider":3460,"published":787,"seo":57579,"stem":57580,"tags":57581,"url":57114,"__hash__":57583},"blog/blog/why-static-sites-are-back.md",{"type":8,"value":57571,"toc":57572},[],{"title":307,"searchDepth":748,"depth":748,"links":57573},[],"When we first built websites many years ago they were very static. It was all we knew. Html with some CSS and a tiny bit of JavaScript. A typical website from 1996, SpaceJam which really shows how static sites are.","v1630862645/debbie.codes/featured-posts/why-static-sites-are-back_tw2bgc",{"loading":3458,"platform":57577},"Microsoft DevTo","/blog/why-static-sites-are-back",{"title":57116,"description":57574},"blog/why-static-sites-are-back",[57582],"jamstack","9b2O2zdSZuhvrsDvoTWPMxOJhGupdDG9IO-PKddbaUM",{"id":57585,"title":57586,"body":57587,"canonical":788,"date":57868,"description":57869,"extension":786,"featured":787,"image":57870,"meta":57871,"navigation":790,"ogimage":788,"path":57873,"provider":3460,"published":790,"seo":57874,"stem":57875,"tags":57876,"url":788,"__hash__":57877},"blog/blog/work-life-balance.md","Finding the Right Work Life Balance",{"type":8,"value":57588,"toc":57847},[57589,57592,57598,57602,57605,57608,57611,57615,57618,57621,57625,57628,57631,57634,57638,57642,57645,57648,57651,57657,57660,57664,57667,57670,57673,57677,57680,57683,57689,57692,57696,57699,57702,57705,57708,57713,57716,57720,57723,57729,57732,57735,57741,57745,57748,57751,57755,57758,57764,57767,57770,57774,57777,57780,57784,57787,57790,57793,57797,57800,57803,57806,57809,57812,57816,57819,57825,57828,57831,57835,57838,57841,57844],[11,57590,57591],{},"Before you start reading this I want you to all know that I do not have all the answers and we do not all live with the same circumstances so these are just my views and experiences on how I have managed my work life balance. To many people it might look like I have it all worked out and to be honest I do think I have a pretty good work life balance. But it wasn't always that way. I am probably older than most people who will read this and therefore I have had more time to make mistakes and learn from them.",[11,57593,57594],{},[121,57595],{"alt":57596,"src":57597},"me running in the forest","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1640965793/debbie.codes/blog/devs-in-the-forest_c_gctzoz.jpg",[23,57599,57601],{"id":57600},"burn-out-at-21","Burn out at 21",[11,57603,57604],{},"When I was 21 I had a great life, but a terrible work life balance. I was living in London and working in a post production company in the TV industry. You may already know that I always wanted to work in TV and at 21 I gave it a go. I had the perfect life. I loved my job so much. I would wake up in the morning dancing going to work. But I was getting paid 7 thousand pounds a year. In London that is not a lot to live on.",[11,57606,57607],{},"So I also had a another fulltime job in a bar. I was working 80 - 90 hours a week. I was also practicing Taekwondo and going for my black belt at the time. And I was out partying quite a bit. I was 21 after all. Perfect life balance. Only I didn't sleep much and I certainly didn't eat well. I had very little money and chose a bottle of wine over a good dinner. Free toast in the studio would do for food. My fridge was constantly empty and I lived on pasta and tomato sauce and if I had enough money for bacon well that was a real treat. Sometimes the production would buy us lunch and that's when I ate properly.",[11,57609,57610],{},"But I didn't care. I was happy. Until I burnt out and was made to realise that I could not live like this. That meant giving up my amazing job in London and moving home to live with my parents. At the time I felt like a complete failure but actually giving up something you love to improve your health is not a failure at all.",[23,57612,57614],{"id":57613},"stress-of-office-hours","Stress of Office Hours",[11,57616,57617],{},"A few years ago while working in tech I had a timetable that was quite flexible so I decided I would start early and finish early. Get the job done and go home kinda thing. Don't get me wrong, I really liked my job but office hours and all that. So I used to get up before 7am and go to work and 3 times a week I would go to cross fit to train for an hour. Then I would come home and shower, grab my pre-made smoothie for my breakfast, my pre-made lunch, my laptop and put it all into my bag, got on my bike and cycled to work. I would then sit in meetings having my breakfast, my smoothie.",[11,57619,57620],{},"I would then do my job which consisted of meetings, leading my team, meetings, emails, jira, consulting, estimating and everything a Tech Lead has to do. I was always busy and always had deadlines and so my lunch, which was in the work fridge, would sometimes sit in the work fridge until I was so hungry I said I better stop and eat. Sometimes I would sit at my desk and eat and sometimes I would completely forget to eat and would work late and be completely starving when I came home. Sometimes I did things right and stopped for lunch but not always.",[138,57622,57624],{"id":57623},"always-working-after-hours","Always working after Hours",[11,57626,57627],{},"When I got home in the evening I would study a lot. I was the Tech Lead making big decisions and trying to push new tech forward such as static sites, JavaScript frameworks and headless CMS's. I needed to be confident when I proposed these solutions and I didn't have enough time in work to research and prototype all this so I would do it at home in my own free time. I would also have meetings, calls or emails with the CEO after hours and at weekends. I was also responsible for this as I would send emails too in evenings and weekends.",[11,57629,57630],{},"I was always working and when I wasn't I was mentoring for OpenClassrooms and Treehouse and teaching English at the weekend cause it was extra money and I just don't know how to say no.",[11,57632,57633],{},"I basically had a terrible work life balance but I felt it was necessary. As a female Tech Lead, and often the only woman in a client meeting, I had to prove myself in every situation. My ideas and thoughts always had to be given with huge confidence and backed up by POC's and I always had to have the answer to every question that was going to be thrown at me. I was determined to succeed and make the correct decisions with regards to tech.",[23,57635,57637],{"id":57636},"working-remote","Working Remote",[138,57639,57641],{"id":57640},"lockdown","Lockdown",[11,57643,57644],{},"I changed jobs and stated working remote just as were moved into lockdown. Everyone has had different and difficult experiences of lockdown and unfortunately some people are still going through this. We were locked in our apartment for 7 weeks with no possibility of doing any sport, no going for a walk, nothing.",[11,57646,57647],{},"I lived for my 8pm balcony party where we would clap for the healthcare workers and dance to two songs that we could hear from the massive speakers of one of the neighbouring blocks. This was the only moment in the whole day that I got to see people. Believe me there was many a moment when I just wanted to jump off the balcony. If this was life forever then it wasn't the one I wanted to live.",[11,57649,57650],{},"That's when I started doing sport on the roof terrace. I tried to run 10km round in circles, changing direction every kilometer to not hurt my ankles. 7 weeks of no sport. No way. I had to do something. I even did Taekwondo classes online. Not ideal but it was fun at the time.",[11,57652,57653],{},[121,57654],{"alt":57655,"src":57656},"me doing taekwondo on the roof with the computer showing the zoom call for training","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652185008/debbie.codes/blog/2022/taekwondo-on-roof_pfj7k3",[11,57658,57659],{},"During lockdown I just worked. Pretty much all day, every day. Even at weekends. I needed something to keep my mind off the world around me so I didn't look at the news until after 8pm. I just pretended the world was normal until then. And I literally just worked.",[138,57661,57663],{"id":57662},"freedom-to-do-sport","Freedom to do Sport",[11,57665,57666],{},"When we finally got let out to do sport, 7 weeks later, it was such a feeling of freedom. Only for 2 hours though. We could only do a maximum of 2 hours so I was out the door and on my bike at 8am and back at exactly 10am. And it was so nice to see life, people, smell things, hear things. It was weird as no children were allowed out. They had a different hour to us so it was like children didn't exist and the usual noise of children playing was not to be heard for at least another month until the time restrictions were removed.",[11,57668,57669],{},"One thing the lockdown thought me is that I literally spent my days just working and living for those 2 hours of freedom, those 2 hours of sport. This was so important to me and something I needed to continue to do. When I look back at the diary I have kept where I wrote down my goals, One of them was simply to do more sport. Why in the past was I not able to achieve this. I needed to better manage my time so that I was working less and doing more sport.",[11,57671,57672],{},"I really wasn't that good at running so putting on my shoes and going for a run took a lot of effort. But when someone takes your freedom away from you and then gives it back you realise that you had something all along and it was actually you that was stopping yourself from being free and from doing the things you love to do.",[23,57674,57676],{"id":57675},"putting-sport-first","Putting Sport First",[11,57678,57679],{},"So that's when I decided to change how I live. I decided to do sport in the morning, every morning for at least an hour but on average for 3 hours. As much as I love sport if I sit on the computer for long enough I will forget to stop and then it's too late to do any sport. Many people say, wow 3 hours. But I don't feel like it's a lot. We spend so much time in front of a screen mainly sitting so being active and on my feet for just 3 hours a day really isn't a lot of time.",[11,57681,57682],{},"I work much better when I get up in the morning and go for a 60km cycle or a 15km run or play a few padel matches. The morning sun helps me appreciate things, clears the mind and sets me up for the day. My sport is normally outdoors and surrounded by nature. Then I know I can go to work and not have to try to stop work to do sport. Cause for some reason I simply keep saying 5 more minutes to myself and then hours pass and it becomes too late to do sport. There are days when I can enjoy an evening run but it doesn't always happen. Morning just works so much better for me.",[11,57684,57685],{},[121,57686],{"alt":57687,"src":57688},"me cycling with other female cyclists","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652182298/debbie.codes/blog/2022/cycling-with-girls_nh2k0q.heic",[11,57690,57691],{},"In order to make sure I kept to my routine I would block time in my calendar for these activities, either the cycles or padel matches etc. If it is set in my calendar then I won't forget to do it even if it's something I love. Once it becomes routine it becomes a new habit that you are simply used to doing.",[138,57693,57695],{"id":57694},"morning-routine","Morning Routine",[11,57697,57698],{},"I start my day at 7am but I will pick up my phone and start reading through emails, slack, twitter etc. I know this is a terrible habit but actually sometimes it just sets me up for the day so will see if I keep that habit or not.",[11,57700,57701],{},"I then make my breakfast which is a smoothie of almond milk, almonds, chocolate, cocoa powder and bananas. I make enough for 4 days so most days I just grab one from the fridge and then go to my office, (the room next to my bedroom) and I sit on the sofa having my smoothie and writing in my 6 minute diary about what I am grateful for, how I can improve and how I am going to make today great. I then write down my dreams and goals and things I want to achieve. Yes I do this every single day.",[11,57703,57704],{},"I then either study, or work for an hour if there is anything urgent, but normally I try to use this time to study, do a course, watch a conference talk etc.",[11,57706,57707],{},"Then at about 9am I head to the gym, sometimes a bit earlier or sometimes a bit later depending on if I have a class or match or a cycle. I run to the gym every day and run home which is only a 1km run but it was a habit I made and have kept for almost 2 years now. I can't believe I used to drive to the gym.",[11,57709,57710],{},[121,57711],{"alt":57596,"src":57712},"https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652182229/debbie.codes/blog/2022/me_running_q64fvs",[11,57714,57715],{},"I then stay in the gym for about 3 hours. I play padel which is similar to tennis and we play outdoors and as a team so not only am I in the fresh air but I am also mixing with people. People and sport give me energy. Sometimes I might go for a 3-4 hour cycle with the club and sometimes I might just go for a 10-15km run.",[138,57717,57719],{"id":57718},"afternoon-routine","Afternoon Routine",[11,57721,57722],{},"At about 12 o'clock I am back home and normally work for an hour. I then start to cook lunch which I actually really enjoy. I get to have lunch with my husband which is something I could never do before. Lunch in Spain is more like a dinner as in a hot meal so it's really nice to cook for two and eat together. At 2pm my husband goes to work and so I head back to my office. I will then work straight through until 9pm or 9:15 rather when my husband gets home and makes sure I am not on the computer.",[11,57724,57725],{},[121,57726],{"alt":57727,"src":57728},"me in my home office with all my screens","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1640970262/debbie.codes/blog/react-conf_c_po1euk.jpg",[11,57730,57731],{},"In the summer it is a lot easier to take breaks cause it is so hot and we have a community pool, so I will often take a 15 minute pool break which is really nice. But outside of summer I am really terrible at taking breaks so I try to do things like stop for a few minutes and empty the dishwasher or hang out the clothes or clean the kitchen. Just taking 5 minutes away from the screen is extremely beneficial.",[11,57733,57734],{},"Although I say this I am still working on improving this. Now that the days are getting nicer I also try to work some hours on the roof terrace. Sometimes changing scenery helps and even just getting up and going to the roof is at least some form of movement. I do have a standing desk though and try to do all my meetings standing so that I am not sitting in the same position for too long.",[11,57736,57737],{},[121,57738],{"alt":57739,"src":57740},"me working in my egg chair on the roof","https://res.cloudinary.com/debsobrien/image/upload/c_scale,f_auto,h_4032,q_auto/v1652182266/debbie.codes/blog/2022/IMG_2873_qxkes9",[138,57742,57744],{"id":57743},"evening-routine","Evening Routine",[11,57746,57747],{},"At 9:15 we start to prepare dinner which is normally some bread and ham and olives or some form of salad or sandwich. Then I am forced to watch 45 minutes of Netflix which is sometimes ok and sometimes just bores me and I find myself reaching for my phone and scrolling through twitter until my husband gives out to me. And at 11pm or just after 11 I go to bed. Then get up the next day and do it all over again.",[11,57749,57750],{},"Obviously sometimes things change. I might not have a match and therefore I might spend less time at the gym in the morning and perhaps go for a cycle or run in the evening. Also, not often, but sometimes it rains in the mornings and when it does, I just work and then go running later when it's dry. Or sometimes I will do an extra long day of working and the next day do an extra long day of sport. The fact that I can easily change my work hours based on the rain is really amazing.",[138,57752,57754],{"id":57753},"weekend-routine","Weekend Routine",[11,57756,57757],{},"At the weekends I don't work. Or at least I try not to. I tend to get up in the mornings and do sport, normally cycling for about 4 hours or running or I may have a padel tournament.",[11,57759,57760],{},[121,57761],{"alt":57762,"src":57763},"padel tournament","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652185183/debbie.codes/blog/2022/padel-tournament_i58mmb.jpg",[11,57765,57766],{},"Then I have lunch and then I normally spend a few hours studying or just programming for fun, improving my site or trying out new things.",[11,57768,57769],{},"In the summer I do tend to go to the beach or sometimes go for a long lunch with a few drinks which means that studying doesn't happen but thats ok. I can always make up for it during the week. I do try to have non computer days sometimes at the weekends but I really just enjoy learning too much.",[23,57771,57773],{"id":57772},"creating-routines","Creating Routines",[11,57775,57776],{},"For me creating these habits and this routine has helped me have time to study and get so much done. It is not always set in stone. Sometimes I will have a mentoring session during my so called work schedule and that means then the next day I will just make up the time in the morning. Being flexible helps as it causes less stress. Having time to do the things you love, which for me is sport, really helps me be more productive.",[11,57778,57779],{},"I feel I have a great work life balance. But everyone is different and really you have to find your own that works for you. Do more of what makes you happy. If you want to do something but don't seem to be able to, then create a new habit to make sure it happens. It could be sport, spending time with the family, reading, whatever.",[23,57781,57783],{"id":57782},"notifications-turned-off","Notifications Turned Off",[11,57785,57786],{},"As for my phone and receiving messages due to time zones I don't think this will be an issue and hasn't been in the past. Although I will admit I did use to answer all slack messages on Sundays and it was hard to change this but simply turning off slack notifications on a Sunday really helps. For context, I worked for a company in Israel whose weekend is Friday and Saturday therefore it was normal that they send messages and emails to me on Sundays.",[11,57788,57789],{},"But in general my phone is always on silent. All day every day. My apple watch gives me notifications and if its important I get my phone and respond and if its not I just ignore it till I finish work. Sorry mam.",[11,57791,57792],{},"All notifications are also turned off always on my computer. I do have a screen open just for slack and discord so I can easily see if there are any messages that need answering without them actually popping up and disturbing me. And when working on the roof with one screen, my watch tells me if there are messages that I need to answer. And if I really need to I can turn on notifications just for certain messaging apps and close non important ones like email etc.",[23,57794,57796],{"id":57795},"things-i-am-not-good-at","Things I am not good at",[11,57798,57799],{},"A couple of things I am not good at. I am terrible at cleaning and I actually pay a cleaner to come twice a month to deep clean the apartment, windows etc. The rest of the time we have a robot hover that does the job. I don't remember the last time I cleaned a floor.",[11,57801,57802],{},"I am terrible at bringing the bins out including the recycling. I always forget this. The bins can only be done at a certain time which is rather annoying and the recycling, I have no excuse, I just haven't created a new habit for it yet.",[11,57804,57805],{},"I never make the bed. I think it's just pointless. I never iron unless there is a top that can't be worn without ironing but then you can be assured I will wear it only a few times a year.",[11,57807,57808],{},"I'm no good at remembering to water plants. They normally all die on me. It takes seconds I know but I just seem to forget about them. I really should improve this and go back to growing the veg and herbs on the roof again.",[11,57810,57811],{},"I'm not a big fan of food shopping but it does have to be done so I find time. Normally at weekends or last thing in the evening if we are really short of something I make a quick trip to the closest shop just before it closes.",[23,57813,57815],{"id":57814},"do-i-have-it-all-figured-out","Do I have it all figured out?",[11,57817,57818],{},"And that's pretty much my day to day, my work life balance. It took me a long time to figure this stuff out and you will too. Just try to do the things you love and prioritise them and then create small habits to make sure you get them done. I am not really sure I am doing things right or if this will change in the future but for now this really works for me.",[11,57820,57821],{},[121,57822],{"alt":57823,"src":57824},"me in a swimming pool","https://res.cloudinary.com/debsobrien/image/upload/f_auto,q_auto/v1652185509/debbie.codes/blog/2022/min-in-pool_mcdqya",[11,57826,57827],{},"I used to start work as early as possible and try to finish as early as possible but always end up working more. And now I do the complete opposite and the only time I work later than 9pm is when there is football on the TV. Much prefer to work than watch football.",[11,57829,57830],{},"I very much prefer this schedule, especially in winter. I get to do sport in day light which is so beneficial. I personally love my routine. It doesn't work for everyone but it certainly works for me. So find the one that best works for you, especially if you have the freedom to choose your own hours. Find the right balance and believe me you will be so much more productive and so much happier.",[23,57832,57834],{"id":57833},"things-to-note","Things to Note",[11,57836,57837],{},"As much as routine is important it is also ok to break it every once in a while. Right now I am injured so can't take part in my normal routine. Stopping also makes me appreciate how much I get done because if it wasn't for routine I would get lost in the computer and not get anything done at all. Not just work but the computer in general, social media etc can really consume us and we can just loose so much time and when we do, that's when we loose our productivity and wonder why.",[11,57839,57840],{},"I do check Twitter quite a lot and sometimes I have to stop myself from scrolling and scrolling and when I catch myself it normally means I need to just get up and walk around. You have to snap yourself out of the moment. I very rarely use Facebook and I don't have Instagram or TikTok simply because I don't want to dedicate more time to social media.",[11,57842,57843],{},"If you ever wonder where your time is going start writing down what you are doing for a week and then look back and see what things you can easily cut out. Take away 2 hours a week of social media and you could easily write a blog post, study something or create a YouTube video. We all have the same 24 hours in the day just some people use their time better than others.",[11,57845,57846],{},"And of course some people who use their time wisely and also bring up kids at the same time are my real heros and I would love to hear how they do it. I feel I have it easy to be honest. I'm also very grateful for where I live and having nature so close to me.",{"title":307,"searchDepth":748,"depth":748,"links":57848},[57849,57850,57853,57857,57863,57864,57865,57866,57867],{"id":57600,"depth":748,"text":57601},{"id":57613,"depth":748,"text":57614,"children":57851},[57852],{"id":57623,"depth":756,"text":57624},{"id":57636,"depth":748,"text":57637,"children":57854},[57855,57856],{"id":57640,"depth":756,"text":57641},{"id":57662,"depth":756,"text":57663},{"id":57675,"depth":748,"text":57676,"children":57858},[57859,57860,57861,57862],{"id":57694,"depth":756,"text":57695},{"id":57718,"depth":756,"text":57719},{"id":57743,"depth":756,"text":57744},{"id":57753,"depth":756,"text":57754},{"id":57772,"depth":748,"text":57773},{"id":57782,"depth":748,"text":57783},{"id":57795,"depth":748,"text":57796},{"id":57814,"depth":748,"text":57815},{"id":57833,"depth":748,"text":57834},"2022-05-10","To many people it might look like I have it all worked out and to be honest I do think I have a pretty good work life balance. But it wasn't always that way. I am probably older than most people who will read this and therefore I have had more time to make mistakes and learn from them.","v1640965793/debbie.codes/blog/devs-in-the-forest_c_gctzoz.jpg",{"ogImage":57872,"loading":3458},"https://res.cloudinary.com/debsobrien/image/upload/f_webp,q_80,c_fit,w_480/v1640965793/debbie.codes/blog/devs-in-the-forest_c_gctzoz.jpg","/blog/work-life-balance",{"title":57586,"description":57869},"blog/work-life-balance",[3464],"h-BDJ2yGmyakxIq_XlwvPi3BzhnGCLhsQafvw-N_taY",1780086036122]