<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Preston Mayieka - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Preston Mayieka - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 11 May 2026 22:39:14 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/preston176/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Develop Chrome Extensions using Plasmo [Full Handbook] ]]>
                </title>
                <description>
                    <![CDATA[ Chrome extensions are lightweight tools that enhance and personalize your browsing experience, whether that's managing passwords, translating pages, or adding entirely new features to websites you use ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-develop-chrome-extensions-using-plasmo-handbook/</link>
                <guid isPermaLink="false">6a0237edfca21b0d4b636175</guid>
                
                    <category>
                        <![CDATA[ chrome extension ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Google Chrome ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Preston Mayieka ]]>
                </dc:creator>
                <pubDate>Mon, 11 May 2026 20:11:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/e0d0bca4-a2e8-495a-9c1c-4f0b9ef52630.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Chrome extensions are lightweight tools that enhance and personalize your browsing experience, whether that's managing passwords, translating pages, or adding entirely new features to websites you use every day.</p>
<p>Millions of developers have published extensions to the Chrome Web Store, and building one is more approachable than you might think.</p>
<p>In this handbook you'll go from zero to a published Chrome extension using TypeScript, React, and Plasmo, a modern framework that handles the repetitive setup and configuration so you can focus on writing features instead of boilerplate.</p>
<p>Along the way you'll touch the real Chrome extension APIs that power production extensions: querying tabs, creating tab groups, and passing messages between different parts of an extension.</p>
<p>By the end you'll have working code, a mental model of how extensions are structured, and everything you need to publish your own ideas to the Chrome Web Store.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-plasmo">What is Plasmo?</a></p>
</li>
<li><p><a href="#heading-what-you-will-build">What You Will Build</a></p>
</li>
<li><p><a href="#heading-what-you-will-learn">What You Will Learn</a></p>
</li>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-understanding-the-background-script">Understanding the Background Script</a></p>
</li>
<li><p><a href="#heading-building-the-popup-ui">Building the Popup UI</a></p>
</li>
<li><p><a href="#heading-testing-your-extension">Testing Your Extension</a></p>
</li>
<li><p><a href="#heading-next-steps-and-extension-ideas">Next Steps and Extension Ideas</a></p>
</li>
<li><p><a href="#heading-deploying-to-chrome-web-store">Deploying to Chrome Web Store</a></p>
</li>
</ul>
<h2 id="heading-what-is-plasmo">What is Plasmo?</h2>
<p><a href="https://www.plasmo.com/">Plasmo</a> is an open-source framework for building browser extensions. Think of it as the equivalent of Create React App or Next.js, but for Chrome extensions.</p>
<p>Without Plasmo, building a Chrome extension requires manually writing a <code>manifest.json</code> file, wiring up build tooling, and configuring TypeScript and React yourself. Plasmo handles all of that.</p>
<p>A single command scaffolds a working project with TypeScript and React already configured. It reads your <code>package.json</code> and generates the <code>manifest.json</code> Chrome requires, so you never edit it directly.</p>
<p>Moreover, changes to your source files automatically rebuild and reload the extension in Chrome during development, and full type safety including types for Chrome's own APIs is available out of the box.</p>
<p>Plasmo doesn't hide the Chrome extension concepts from you. You still use <code>chrome.tabs</code>, <code>chrome.runtime</code>, and the rest of the Chrome APIs directly. It just removes the tedious scaffolding so you can start building immediately.</p>
<h2 id="heading-what-you-will-build">What You Will Build</h2>
<p>In this tutorial, you'll build a <strong>Tab Grouper</strong> Chrome extension from scratch.</p>
<p>This extension automatically organizes your browser tabs by grouping them based on their website domain.</p>
<img src="https://cdn.hashnode.com/uploads/covers/64ef9ca6a3a26476fe998b69/43f51cde-41c8-46ac-9305-6b4ad5adc1ac.gif" alt="Animated demo of the Tab Grouper extension grouping open tabs into colored groups by domain" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-example-use-case">Example Use Case</h3>
<p>Imagine you have 20 tabs open: 5 from GitHub, 4 from YouTube, 3 from Stack Overflow, and 8 from other websites.</p>
<p>With one click, the Tab Grouper extension will automatically create colored groups for each website, making it straightforward to find and manage your tabs.</p>
<h2 id="heading-what-you-will-learn">What You Will Learn</h2>
<p>By completing this tutorial, you'll get hands-on experience in three areas.</p>
<p>First, <strong>Chrome Extension Basics</strong>: how extensions work under the hood, the anatomy of an extension (manifest, background scripts, popups), and how to load and test extensions in Chrome during development.</p>
<p>Second, <strong>Chrome APIs</strong>: specifically <code>chrome.tabs</code> for managing browser tabs, <code>chrome.tabGroups</code> for creating and customizing tab groups, and <code>chrome.runtime</code> for passing messages between different parts of your extension.</p>
<p>Third, <strong>Modern Web Development tooling</strong>: TypeScript for type-safe JavaScript, React for building the popup UI, and the Plasmo framework that ties it all together.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You don't need to be an expert in any of these, but you'll have the smoothest experience if you're comfortable with basic JavaScript or TypeScript and have a general understanding of HTML and CSS.</p>
<p>Some familiarity with React is helpful but not required. The pop-up component we'll build is simple enough to follow even if you're new to it.</p>
<p>On the software side, you'll need Node.js version 18 or higher (<a href="https://nodejs.org/">download here</a>), Google Chrome, a code editor (VS Code is recommended), and pnpm as your package manager.</p>
<h3 id="heading-verify-your-setup">Verify Your Setup</h3>
<p>Open your terminal and run these commands to confirm everything is installed:</p>
<pre><code class="language-bash">node --version
# Should output v18.0.0 or higher

npm --version
# Should output 9.0.0 or higher
</code></pre>
<h3 id="heading-getting-help">Getting Help</h3>
<p>If you get stuck, review the complete code in the repository, consult the Chrome Extension documentation, or ask for help in the community forums.</p>
<h3 id="heading-ready-to-begin">Ready to Begin?</h3>
<p>In the next section, you'll set up your development environment and create your first Chrome extension project.</p>
<p>Let's get started!</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>In this section, you'll use Plasmo to scaffold your Chrome extension project, then customize it for the Tab Grouper.</p>
<p>Rather than creating files manually, you'll let Plasmo generate a starter project with all required configuration, then explore what was created before customizing it for our needs.</p>
<h2 id="heading-step-1-install-pnpm-recommended">Step 1: Install pnpm (Recommended)</h2>
<p>Plasmo officially recommends <strong>pnpm</strong> for faster installs and better disk space usage. Check if you already have it:</p>
<pre><code class="language-bash">pnpm --version
</code></pre>
<p>If you see a version number, skip to Step 2.</p>
<img src="https://cdn.hashnode.com/uploads/covers/64ef9ca6a3a26476fe998b69/aeed7b06-a403-4fe2-81fe-571a00219acf.png" alt="Terminal output showing pnpm version number after running pnpm --version" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>If you get "command not found", install it with:</p>
<pre><code class="language-bash">npm install -g pnpm
</code></pre>
<h2 id="heading-step-2-create-your-extension-project">Step 2: Create Your Extension Project</h2>
<p>Run this command to create a new Plasmo project:</p>
<pre><code class="language-bash">pnpm create plasmo tab-grouper
</code></pre>
<p>You'll see:</p>
<pre><code class="language-plaintext">🟣 Creating a new Plasmo extension
📁 Project name: tab-grouper
? Extension description: (Give your extension a nice description)
? Author name: (Your Name)
</code></pre>
<p>Plasmo will then scaffold the project and install dependencies automatically. You might be prompted to enter a description and author name.</p>
<p>Fill these in however you like.</p>
<img src="https://cdn.hashnode.com/uploads/covers/64ef9ca6a3a26476fe998b69/e0a58818-0bec-42a7-bde3-c7a66de68b7a.png" alt="Terminal output showing Plasmo scaffolding a new project called tab-grouper and installing dependencies." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-step-3-navigate-to-your-project">Step 3: Navigate to Your Project</h3>
<pre><code class="language-bash">cd tab-grouper
</code></pre>
<h3 id="heading-step-4-explore-what-was-created">Step 4: Explore What Was Created</h3>
<p>List the files that Plasmo generated:</p>
<pre><code class="language-bash">ls -la
</code></pre>
<p>You should see something like this:</p>
<pre><code class="language-plaintext">tab-grouper/
├── .git/                 # Git repository (already initialized!)
├── .github/              # GitHub Actions workflows
├── assets/
│   └── icon.png          # Default Plasmo icon 
├── node_modules/         # Dependencies (already installed!)
├── package.json          # Project configuration
├── popup.tsx             # Default popup 
├── .prettierrc.cjs       # Code formatting rules
├── .gitignore            # Git ignore rules
├── README.md             # Default readme
└── tsconfig.json         # TypeScript configuration
</code></pre>
<p>The key files to know about:</p>
<ul>
<li><p><strong>assets/icon.png</strong>: The extension icon required by Chrome.</p>
</li>
<li><p><strong>package.json</strong>: Lists dependencies and scripts, and is where you configure the extension manifest.</p>
</li>
<li><p><strong>popup.tsx</strong>: The UI that appears when you click the extension icon.</p>
</li>
<li><p><strong>tsconfig.json</strong>: Contains TypeScript settings that are already correctly configured.</p>
</li>
</ul>
<h3 id="heading-step-5-test-the-default-extension">Step 5: Test the Default Extension</h3>
<p>Make sure everything works <strong>before</strong> you customize it.</p>
<p>You can do this by starting the development server:</p>
<pre><code class="language-bash">pnpm dev
</code></pre>
<p>You should see output like this:</p>
<pre><code class="language-plaintext">🟣 Plasmo v0.90.5
🔴 The Browser Extension Framework
🔵 INFO   | Starting the extension development server...
🔵 INFO   | Building for target: chrome-mv3
🔵 INFO   | Loaded environment variables from: []
🟢 DONE   | Extension re-packaged in 1842ms! 🚀

View Extension:
📦 build/chrome-mv3-dev
</code></pre>
<p>Your extension is ready. Keep this terminal window open.</p>
<p>Plasmo watches for file changes and rebuilds automatically.</p>
<h3 id="heading-step-6-load-the-extension-in-chrome">Step 6: Load the Extension in Chrome</h3>
<p>Now load the extension into Chrome to test it:</p>
<ol>
<li><p>Open Google Chrome</p>
</li>
<li><p>Go to <code>chrome://extensions/</code></p>
</li>
<li><p>Enable <strong>Developer mode</strong> (toggle in top-right)</p>
</li>
<li><p>Click <strong>"Load unpacked"</strong></p>
</li>
<li><p>Navigate to your project folder</p>
</li>
<li><p>Select the <code>build/chrome-mv3-dev</code> folder</p>
</li>
<li><p>Click "Select Folder"</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/64ef9ca6a3a26476fe998b69/19cef596-a9d1-4709-8d27-594381d03842.gif" alt="Animated gif showing how to load an unpacked extension in Chrome via the Extensions page developer mode" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Your extension should now appear in the list.</p>
<h3 id="heading-step-7-test-the-default-popup">Step 7: Test the Default Popup</h3>
<ol>
<li><p>Click the puzzle piece icon in Chrome's toolbar</p>
</li>
<li><p>Find "tab-grouper" and pin it</p>
</li>
<li><p>Click the extension icon</p>
</li>
</ol>
<p>You will see a default popup that says "Welcome to Plasmo!"</p>
<img src="https://cdn.hashnode.com/uploads/covers/64ef9ca6a3a26476fe998b69/56bad298-b07e-41c5-a648-49e382e0c51b.png" alt="The default Plasmo popup showing a Welcome to Plasmo message in the Chrome toolbar popup" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The extension is working. Now you can customize it.</p>
<h3 id="heading-step-8-update-extension-information">Step 8: Update Extension Information</h3>
<p>Open <code>package.json</code> in your editor. This file stores metadata about your project. name, version, description, dependencies, and scripts for building and running your extension.</p>
<p>Find these lines near the top:</p>
<pre><code class="language-json">{
  "name": "tab-grouper",
  "displayName": "tab-grouper",
  "version": "0.0.0",
  "description": "A basic Plasmo extension.",
</code></pre>
<p>Change them to:</p>
<pre><code class="language-json">{
  "name": "tab-grouper",
  "displayName": "Tab Grouper",
  "version": "1.0.0",
  "description": "A simple Chrome extension - group tabs by domain",
</code></pre>
<p>Save the file.</p>
<h3 id="heading-step-9-add-required-permissions-critical">Step 9: Add Required Permissions (Critical!)</h3>
<p><strong>This is a critical step.</strong> Without permissions, your extension will fail with errors like:</p>
<pre><code class="language-plaintext">TypeError: Cannot read properties of undefined (reading 'query')
</code></pre>
<p>Chrome extensions must declare which browser APIs they intend to use. In <code>package.json</code>, find the <code>"manifest"</code> section.</p>
<p>It looks like this:</p>
<pre><code class="language-json">"manifest": {
  "host_permissions": [
    "https://*/*"
  ]
}
</code></pre>
<p>Replace it with:</p>
<pre><code class="language-json">"manifest": {
  "permissions": [
    "tabs",
    "tabGroups"
  ]
}
</code></pre>
<p>Save the file. The <code>tabs</code> permission allows you to read tab information (required for <code>chrome.tabs.query()</code>), and <code>tabGroups</code> allows you to create and manage tab groups (required for <code>chrome.tabGroups.update()</code>).</p>
<h3 id="heading-finding-the-right-permissions-for-your-own-extensions">Finding the right permissions for your own extensions:</h3>
<p>The <a href="https://developer.chrome.com/docs/extensions/reference/permissions-list">Chrome Extension Permissions Reference</a> lists every available permission and what it unlocks.</p>
<p>Each API's documentation page also lists which permissions it requires, for example, the <a href="https://developer.chrome.com/docs/extensions/reference/api/tabs">chrome.tabs API page</a> specifies the <code>"tabs"</code> permission.</p>
<p>If you're using Plasmo, the <a href="https://docs.plasmo.com/framework/customization/manifest">Manifest Configuration docs</a> explain how to add permissions through <code>package.json</code>.</p>
<p>As a general rule: if you're getting <code>undefined</code> errors when calling a Chrome API, a missing permission is the first thing to check.</p>
<h3 id="heading-step-10-verify-hot-reload-works">Step 10: Verify Hot Reload Works</h3>
<p>Plasmo automatically reloads your extension when you save changes.</p>
<p>Check the terminal where <code>pnpm dev</code> is running. After saving <code>package.json</code> you should see something like:</p>
<pre><code class="language-plaintext">🔄 Reloading extension...
✅ Ready in 0.8s
</code></pre>
<p>Your project is now ready: a working extension loaded in Chrome, a development server running with hot reload, and the required permissions in place.</p>
<p>Leave the dev server running and the extension loaded as you work through the next sections. Your changes will reload automatically.</p>
<h3 id="heading-section-summary">Section Summary</h3>
<p>In this section you installed pnpm, scaffolded a new extension with <code>pnpm create plasmo</code>, explored the generated project structure, started the development server, loaded the extension in Chrome, and updated the extension metadata and permissions.</p>
<p><strong>Next:</strong> You'll create the background script that handles the tab grouping logic.</p>
<h2 id="heading-understanding-the-background-script">Understanding the Background Script</h2>
<p>The background script is the heart of your extension. It runs persistently behind the scenes and contains the core logic.</p>
<p>In this case, the code that groups your tabs by domain.</p>
<h3 id="heading-what-is-a-background-script">What is a Background Script?</h3>
<p>A background script runs continuously even when the popup is closed.</p>
<p>It can listen to browser events like tabs opening, closing, or updating, perform tasks that don't require direct user interaction, and communicate with other parts of the extension by passing messages.</p>
<p>Think of it as the server-side of your extension. The popup is just a UI that talks to it.</p>
<h3 id="heading-step-1-create-backgroundts">Step 1: Create background.ts</h3>
<p>Plasmo's scaffolding didn't create a background script by default, so you'll create this file from scratch. Create a new file called <code>background.ts</code> in your project root (the same level as <code>popup.tsx</code>):</p>
<pre><code class="language-typescript">export {}

// Background script - runs in the background and handles tab grouping logic

console.log("Tab Grouper background script loaded!")

// Listen for messages from the popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) =&gt; {
  if (message.type === "GROUP_TABS") {
    groupTabsByDomain()
    sendResponse({ success: true })
  }
  return true
})
</code></pre>
<p>The <code>export {}</code> at the top is required by Plasmo to treat this file as a module. Without it you may get errors about conflicting global variable declarations.</p>
<p>The <code>console.log</code> will help you verify the script loaded correctly (you'll see it in the extension's DevTools console). <code>chrome.runtime.onMessage</code> sets up a listener so the background script can receive instructions from the popup.</p>
<p>When it receives a <code>"GROUP_TABS"</code> message, it calls the grouping function.</p>
<p>You can read more about this messaging pattern in the <a href="https://developer.chrome.com/docs/extensions/develop/concepts/messaging">Chrome Extensions documentation</a>.</p>
<h3 id="heading-step-2-implement-tab-grouping-logic">Step 2: Implement Tab Grouping Logic</h3>
<p>Now add the main grouping function below the message listener:</p>
<pre><code class="language-typescript">async function groupTabsByDomain() {
  try {
    // Step 1: Get all tabs in the current window
    const tabs = await chrome.tabs.query({ currentWindow: true })

    // Step 2: Create a Map to organize tabs by domain
    const domainGroups = new Map&lt;string, chrome.tabs.Tab[]&gt;()

    // Step 3: Loop through each tab and group by domain
    tabs.forEach(tab =&gt; {
      // Skip tabs without URLs
      if (!tab.url) return

      // Extract the domain from the URL
      const domain = getDomainFromUrl(tab.url)

      // Skip invalid domains (like chrome:// pages)
      if (!domain) return

      // Add tab to the appropriate domain group
      if (!domainGroups.has(domain)) {
        domainGroups.set(domain, [])
      }
      domainGroups.get(domain)!.push(tab)
    })

    // Step 4: Create tab groups for each domain (only if 2+ tabs)
    for (const [domain, domainTabs] of domainGroups) {
      // Skip domains with only 1 tab
      if (domainTabs.length &lt; 2) continue

      // Get all tab IDs
      const tabIds = domainTabs
        .map(t =&gt; t.id!)
        .filter(id =&gt; id !== undefined)

      if (tabIds.length === 0) continue

      // Create the tab group
      const groupId = await chrome.tabs.group({ tabIds })

      // Customize the group with a title and color
      await chrome.tabGroups.update(groupId, {
        title: domain,
        color: getColorForDomain(domain) // Randomized Tab Group colors.
      })
    }

    console.log(`Successfully grouped ${domainGroups.size} domains`)
  } catch (error) {
    console.error("Error grouping tabs:", error)
  }
}
</code></pre>
<p>The function starts by querying all tabs in the current window, then iterates over them to build a <code>Map</code> keyed by domain name.</p>
<p>Once every tab has been sorted into a domain bucket, it loops through the map and calls <code>chrome.tabs.group()</code> for any domain that has two or more tabs, then immediately customizes the resulting group with a title and color.</p>
<p>Domains with only a single tab are skipped. There's no point grouping a lone tab.</p>
<h3 id="heading-step-3-extract-domain-helper">Step 3: Extract Domain Helper</h3>
<p>Add a helper function to pull the hostname out of a URL:</p>
<pre><code class="language-typescript">function getDomainFromUrl(url: string): string | null {
  try {
    const urlObj = new URL(url)

    // Skip Chrome internal pages (chrome://, chrome-extension://)
    if (urlObj.protocol === "chrome:" || urlObj.protocol === "chrome-extension:") {
      return null
    }

    // Remove "www." prefix and return the hostname
    return urlObj.hostname.replace(/^www\./, "")
  } catch {
    // Return null if URL is invalid
    return null
  }
}
</code></pre>
<p><code>new URL(url)</code> gives us a structured object to work with rather than string-parsing the URL manually.</p>
<p>The protocol check filters out Chrome's internal pages like <code>chrome://extensions</code> and <code>chrome://settings</code>, which extensions can't access.</p>
<p>The <code>.replace(/^www\./, "")</code> ensures that <code>www.github.com</code> and <code>github.com</code> are treated as the same domain rather than two separate groups.</p>
<p>The whole thing is wrapped in a try-catch so malformed URLs simply return <code>null</code> and get skipped.</p>
<p>In practice: <code>https://www.github.com/user/repo</code> becomes <code>github.com</code>, <code>https://youtube.com/watch?v=123</code> becomes <code>youtube.com</code>, and <code>chrome://extensions</code> returns <code>null</code>.</p>
<h3 id="heading-step-4-color-assignment-helper">Step 4: Color Assignment Helper</h3>
<p>Add a function to deterministically assign a color to each domain:</p>
<pre><code class="language-typescript">function getColorForDomain(domain: string): chrome.tabGroups.ColorEnum {
  // Available colors in Chrome
  const colors: chrome.tabGroups.ColorEnum[] = [
    "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"
  ]

  // Create a simple hash from the domain name
  let hash = 0
  for (let i = 0; i &lt; domain.length; i++) {
    hash = domain.charCodeAt(i) + ((hash &lt;&lt; 5) - hash)
  }

  // Return a color based on the hash
  return colors[Math.abs(hash) % colors.length]
}
</code></pre>
<p>Chrome supports eight colors for tab groups. Rather than assigning them randomly (which would change every time you group), this function hashes the domain name to a number and uses the modulo operator to pick a consistent index into the color array.</p>
<p>The result is that <code>github.com</code> always gets the same color across sessions, while different domains are likely to get different colors.</p>
<h3 id="heading-complete-backgroundts-file">Complete background.ts File</h3>
<p>Your complete <code>background.ts</code> should look like this:</p>
<pre><code class="language-typescript">export {}

console.log("Tab Grouper background script loaded!")

chrome.runtime.onMessage.addListener((message, sender, sendResponse) =&gt; {
  if (message.type === "GROUP_TABS") {
    groupTabsByDomain()
    sendResponse({ success: true })
  }
  return true
})

async function groupTabsByDomain() {
  try {
    const tabs = await chrome.tabs.query({ currentWindow: true })
    const domainGroups = new Map&lt;string, chrome.tabs.Tab[]&gt;()

    tabs.forEach(tab =&gt; {
      if (!tab.url) return
      const domain = getDomainFromUrl(tab.url)
      if (!domain) return

      if (!domainGroups.has(domain)) {
        domainGroups.set(domain, [])
      }
      domainGroups.get(domain)!.push(tab)
    })

    for (const [domain, domainTabs] of domainGroups) {
      if (domainTabs.length &lt; 2) continue

      const tabIds = domainTabs
        .map(t =&gt; t.id!)
        .filter(id =&gt; id !== undefined)

      if (tabIds.length === 0) continue

      const groupId = await chrome.tabs.group({ tabIds })

      await chrome.tabGroups.update(groupId, {
        title: domain,
        color: getColorForDomain(domain)
      })
    }

    console.log(`Successfully grouped ${domainGroups.size} domains`)
  } catch (error) {
    console.error("Error grouping tabs:", error)
  }
}

function getDomainFromUrl(url: string): string | null {
  try {
    const urlObj = new URL(url)
    if (urlObj.protocol === "chrome:" || urlObj.protocol === "chrome-extension:") {
      return null
    }
    return urlObj.hostname.replace(/^www\./, "")
  } catch {
    return null
  }
}

function getColorForDomain(domain: string): chrome.tabGroups.ColorEnum {
  const colors: chrome.tabGroups.ColorEnum[] = [
    "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"
  ]

  let hash = 0
  for (let i = 0; i &lt; domain.length; i++) {
    hash = domain.charCodeAt(i) + ((hash &lt;&lt; 5) - hash)
  }

  return colors[Math.abs(hash) % colors.length]
}
</code></pre>
<h3 id="heading-testing-the-background-script">Testing the Background Script</h3>
<p>If your development server isn't already running from the previous section, start it:</p>
<pre><code class="language-bash">pnpm dev
</code></pre>
<p>To verify the background script loaded correctly, go to <code>chrome://extensions</code>, find "Tab Grouper Tutorial", and click the <strong>"service worker"</strong> link.</p>
<p>A DevTools console will open and you should see "Tab Grouper background script loaded!" confirming everything is wired up.</p>
<h2 id="heading-building-the-popup-ui">Building the Popup UI</h2>
<p>The popup is the small window that appears when a user clicks your extension icon in the Chrome toolbar.</p>
<p>It can display information, provide buttons for actions, and show settings.</p>
<p>In this section you'll build a React-based popup that shows live tab statistics and triggers the grouping logic in the background script.</p>
<h3 id="heading-step-1-replace-popuptsx">Step 1: Replace popup.tsx</h3>
<p>When you ran <code>pnpm create plasmo</code>, a default <code>popup.tsx</code> was created that just displays a welcome message.</p>
<p>Open that file and replace <strong>all</strong> of its contents with this starting skeleton:</p>
<pre><code class="language-tsx">import { useState, useEffect } from "react"

function IndexPopup() {
  const [tabCount, setTabCount] = useState(0)
  const [groupCount, setGroupCount] = useState(0)
  const [isGrouping, setIsGrouping] = useState(false)

  return (
    &lt;div&gt;
      &lt;h2&gt;Tab Grouper&lt;/h2&gt;
      &lt;button&gt;Group Tabs&lt;/button&gt;
    &lt;/div&gt;
  )
}

export default IndexPopup
</code></pre>
<p>Save the file and the extension will automatically reload.</p>
<p>The three state variables track the number of open tabs, the number of existing groups, and whether a grouping operation is currently in progress.</p>
<p>That last one lets us disable the button and show a loading state so users can't trigger multiple groupings at once.</p>
<h3 id="heading-step-2-load-statistics">Step 2: Load Statistics</h3>
<p>Now add the logic to load tab and group counts when the popup opens. Add this inside the <code>IndexPopup</code> function, right after the state declarations:</p>
<pre><code class="language-tsx">// Load tab statistics when popup opens
useEffect(() =&gt; {
  loadStats()
}, [])

async function loadStats() {
  const tabs = await chrome.tabs.query({ currentWindow: true })
  const groups = await chrome.tabGroups.query({
    windowId: chrome.windows.WINDOW_ID_CURRENT
  })

  setTabCount(tabs.length)
  setGroupCount(groups.length)
}
</code></pre>
<p>The <code>useEffect</code> with an empty dependency array <code>[]</code> runs once when the component first mounts. In other words, every time the popup opens.</p>
<p>It calls <code>loadStats</code>, which queries Chrome for the current window's tabs and groups, then updates the state variables with the counts.</p>
<h3 id="heading-step-3-trigger-tab-grouping">Step 3: Trigger Tab Grouping</h3>
<p>Add the handler that sends a message to the background script when the button is clicked:</p>
<pre><code class="language-tsx">async function handleGroupTabs() {
  setIsGrouping(true)

  // Send message to background script
  await chrome.runtime.sendMessage({ type: "GROUP_TABS" })

  // Refresh statistics
  await loadStats()
  setIsGrouping(false)
}
</code></pre>
<p><code>chrome.runtime.sendMessage</code> delivers the <code>{ type: "GROUP_TABS" }</code> message to the listener we set up in <code>background.ts</code>.</p>
<p>After the background script finishes, we reload the statistics so the group count updates immediately, then re-enable the button.</p>
<h3 id="heading-step-4-build-the-ui">Step 4: Build the UI</h3>
<p>Replace the placeholder <code>return</code> statement with this complete, styled version:</p>
<pre><code class="language-tsx">return (
  &lt;div style={{
    width: 300,
    padding: 20,
    fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
  }}&gt;
    {/* Header */}
    &lt;div style={{ marginBottom: 20 }}&gt;
      &lt;h2 style={{ margin: 0, fontSize: 20, fontWeight: 600 }}&gt;
        🗂️ Tab Grouper
      &lt;/h2&gt;
      &lt;p style={{ margin: "8px 0 0", fontSize: 13, color: "#666" }}&gt;
        Organize your tabs by domain
      &lt;/p&gt;
    &lt;/div&gt;

    {/* Statistics */}
    &lt;div style={{
      display: "flex",
      gap: 12,
      marginBottom: 20,
      padding: 12,
      background: "#f5f5f5",
      borderRadius: 8
    }}&gt;
      &lt;div style={{ flex: 1 }}&gt;
        &lt;div style={{ fontSize: 24, fontWeight: 600, color: "#333" }}&gt;
          {tabCount}
        &lt;/div&gt;
        &lt;div style={{ fontSize: 12, color: "#666" }}&gt;
          Open Tabs
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div style={{ flex: 1 }}&gt;
        &lt;div style={{ fontSize: 24, fontWeight: 600, color: "#0066ff" }}&gt;
          {groupCount}
        &lt;/div&gt;
        &lt;div style={{ fontSize: 12, color: "#666" }}&gt;
          Tab Groups
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    {/* Group Button */}
    &lt;button
      onClick={handleGroupTabs}
      disabled={isGrouping}
      style={{
        width: "100%",
        padding: "12px 16px",
        fontSize: 14,
        fontWeight: 500,
        color: "white",
        background: isGrouping ? "#ccc" : "#0066ff",
        border: "none",
        borderRadius: 8,
        cursor: isGrouping ? "not-allowed" : "pointer",
        transition: "background 0.2s"
      }}
    &gt;
      {isGrouping ? "Grouping..." : "🗂️ Group Tabs by Domain"}
    &lt;/button&gt;

    {/* Footer */}
    &lt;div style={{
      marginTop: 16,
      padding: 12,
      fontSize: 12,
      color: "#666",
      background: "#fff9e6",
      borderRadius: 6,
      border: "1px solid #ffe066"
    }}&gt;
      💡 &lt;strong&gt;Tip:&lt;/strong&gt; This will group all tabs in this window by their website domain.
    &lt;/div&gt;
  &lt;/div&gt;
)
</code></pre>
<p>The UI has four parts: a header with the extension title and a short description, a statistics box showing the live tab and group counts side by side, the main action button (which grays out and changes text to "Grouping..." while work is in progress), and a tip box at the bottom.</p>
<p>This tutorial uses inline styles for simplicity. In a production extension, you'd likely reach for CSS modules, Tailwind, or styled-components instead.</p>
<h3 id="heading-complete-popuptsx-file">Complete popup.tsx File</h3>
<p>Your complete <code>popup.tsx</code> should look like this:</p>
<pre><code class="language-tsx">import { useState, useEffect } from "react"

function IndexPopup() {
  const [tabCount, setTabCount] = useState(0)
  const [groupCount, setGroupCount] = useState(0)
  const [isGrouping, setIsGrouping] = useState(false)

  useEffect(() =&gt; {
    loadStats()
  }, [])

  async function loadStats() {
    const tabs = await chrome.tabs.query({ currentWindow: true })
    const groups = await chrome.tabGroups.query({
      windowId: chrome.windows.WINDOW_ID_CURRENT
    })

    setTabCount(tabs.length)
    setGroupCount(groups.length)
  }

  async function handleGroupTabs() {
    setIsGrouping(true)
    await chrome.runtime.sendMessage({ type: "GROUP_TABS" })
    await loadStats()
    setIsGrouping(false)
  }

  return (
    &lt;div style={{
      width: 300,
      padding: 20,
      fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
    }}&gt;
      &lt;div style={{ marginBottom: 20 }}&gt;
        &lt;h2 style={{ margin: 0, fontSize: 20, fontWeight: 600 }}&gt;
          🗂️ Tab Grouper
        &lt;/h2&gt;
        &lt;p style={{ margin: "8px 0 0", fontSize: 13, color: "#666" }}&gt;
          Organize your tabs by domain
        &lt;/p&gt;
      &lt;/div&gt;

      &lt;div style={{
        display: "flex",
        gap: 12,
        marginBottom: 20,
        padding: 12,
        background: "#f5f5f5",
        borderRadius: 8
      }}&gt;
        &lt;div style={{ flex: 1 }}&gt;
          &lt;div style={{ fontSize: 24, fontWeight: 600, color: "#333" }}&gt;
            {tabCount}
          &lt;/div&gt;
          &lt;div style={{ fontSize: 12, color: "#666" }}&gt;
            Open Tabs
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div style={{ flex: 1 }}&gt;
          &lt;div style={{ fontSize: 24, fontWeight: 600, color: "#0066ff" }}&gt;
            {groupCount}
          &lt;/div&gt;
          &lt;div style={{ fontSize: 12, color: "#666" }}&gt;
            Tab Groups
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;button
        onClick={handleGroupTabs}
        disabled={isGrouping}
        style={{
          width: "100%",
          padding: "12px 16px",
          fontSize: 14,
          fontWeight: 500,
          color: "white",
          background: isGrouping ? "#ccc" : "#0066ff",
          border: "none",
          borderRadius: 8,
          cursor: isGrouping ? "not-allowed" : "pointer",
          transition: "background 0.2s"
        }}
      &gt;
        {isGrouping ? "Grouping..." : "🗂️ Group Tabs by Domain"}
      &lt;/button&gt;

      &lt;div style={{
        marginTop: 16,
        padding: 12,
        fontSize: 12,
        color: "#666",
        background: "#fff9e6",
        borderRadius: 6,
        border: "1px solid #ffe066"
      }}&gt;
        💡 &lt;strong&gt;Tip:&lt;/strong&gt; This will group all tabs in this window by their website domain.
      &lt;/div&gt;
    &lt;/div&gt;
  )
}

export default IndexPopup
</code></pre>
<h2 id="heading-testing-your-extension">Testing Your Extension</h2>
<p>Now that you have both the background script and popup UI built, it's time to verify that everything works together in Chrome.</p>
<h3 id="heading-step-1-make-sure-the-dev-server-is-running">Step 1: Make Sure the Dev Server is Running</h3>
<p>If <code>pnpm dev</code> isn't already running from an earlier step, start it now:</p>
<pre><code class="language-bash">pnpm run dev # or pnpm dev
</code></pre>
<p>Plasmo will build the extension into <code>build/chrome-mv3-dev</code> and watch for changes.</p>
<h3 id="heading-step-2-load-the-extension-in-chrome">Step 2: Load the Extension in Chrome</h3>
<p>If you haven't already loaded the extension, go to <code>chrome://extensions/</code>, enable <strong>Developer mode</strong>, click <strong>Load unpacked</strong>, and select the <code>build/chrome-mv3-dev</code> folder.</p>
<p>Once loaded you should see the extension listed with the name "Tab Grouper Tutorial", version "1.0.0", and status Enabled.</p>
<h3 id="heading-step-3-pin-the-extension">Step 3: Pin the Extension</h3>
<p>Click the puzzle piece icon in the Chrome toolbar, find "Tab Grouper Tutorial", and click the pin icon to keep it visible.</p>
<p>The extension icon will now appear directly in your toolbar.</p>
<h3 id="heading-step-4-test-the-extension">Step 4: Test the Extension</h3>
<h4 id="heading-test-1-open-multiple-tabs">Test 1: Open Multiple Tabs</h4>
<p>Open several tabs across a few domains so there's something to group:</p>
<ol>
<li><p><code>https://github.com/topics</code>, <code>https://github.com/trending</code>, <code>https://github.com/explore</code></p>
</li>
<li><p><code>https://www.youtube.com/</code> and <code>https://www.youtube.com/trending</code></p>
</li>
<li><p><code>https://stackoverflow.com/questions</code> and <code>https://stackoverflow.com/tags</code></p>
</li>
</ol>
<p>Have at least 7 tabs open.</p>
<h4 id="heading-test-2-group-the-tabs">Test 2: Group the Tabs</h4>
<p>Click the Tab Grouper extension icon. The popup should appear showing your open tab count (7 or more) and group count (probably 0).</p>
<p>Click <strong>"Group Tabs by Domain"</strong> and watch your tabs get organized into colored groups.</p>
<h4 id="heading-test-3-verify-groups">Test 3: Verify Groups</h4>
<p>After clicking the button, GitHub tabs should be grouped together with a label like "github.com" and a consistent color, and YouTube tabs similarly.</p>
<p>Click the extension icon again, the group count should now show 2, while the tab count stays the same.</p>
<h3 id="heading-step-5-debug-the-extension">Step 5: Debug the Extension</h3>
<p>If something doesn't work, Chrome's DevTools are your best friend.</p>
<p>To inspect the background script, go to <code>chrome://extensions/</code>, find your extension, and click the <strong>"service worker"</strong> link.</p>
<p>A DevTools console opens where you can look for the "Tab Grouper background script loaded!" message and any error output in red.</p>
<p>To inspect the popup, right-click the extension icon and select <strong>"Inspect popup"</strong>. This opens DevTools for the popup specifically — check the Console tab for any errors there.</p>
<p><strong>If nothing happens when you click the button</strong>, check the background script console for errors, confirm you have at least 2 tabs from the same domain, and verify the message is being sent (look in the popup console for any <code>sendMessage</code> failures).</p>
<p><strong>If tabs aren't grouping</strong>, double-check that you added the <code>tabs</code> and <code>tabGroups</code> permissions to <code>package.json</code> and reloaded the extension after saving.</p>
<p><strong>If you see "Extension cannot access chrome://..."</strong>, that's expected behavior — extensions can't interact with Chrome's internal pages and the code skips them intentionally.</p>
<h3 id="heading-step-6-hot-reloading">Step 6: Hot Reloading</h3>
<p>One of the benefits of Plasmo is hot reloading, which allows you to update code in a running app instantly without needing to restart it manually.</p>
<p>Open <code>popup.tsx</code>, change the header emoji from 🗂️ to 📁, and save.</p>
<p>The extension reloads automatically.</p>
<p>Click the icon and you'll see the updated emoji immediately.</p>
<p>Hot reloading is advantageous because it speeds up development by letting you see changes in real time.</p>
<p>You can change the emoji back afterward if you'd like to keep the extension consistent with the rest of the tutorial examples and screenshots.</p>
<h3 id="heading-step-7-test-edge-cases">Step 7: Test Edge Cases</h3>
<p>It's worth testing a few scenarios to make sure the extension handles them gracefully.</p>
<p>If you close all tabs except one and click "Group Tabs", nothing should happen. The extension requires at least two tabs from the same domain to form a group. Opening <code>chrome://extensions</code> and <code>chrome://settings</code> and then grouping should also do nothing, since those pages are filtered out.</p>
<p>If you have one tab from <code>reddit.com</code> and one from <code>freecodecamp.org</code>, each domain appearing only once, no groups should be created.</p>
<h3 id="heading-step-8-production-build">Step 8: Production Build</h3>
<p>When you're ready to share your extension, run:</p>
<pre><code class="language-bash">pnpm run build
</code></pre>
<p>This creates a production-optimized version in <code>build/chrome-mv3-prod</code>, minified JavaScript, no development-only code, and smaller file size.</p>
<p>To verify the production build, go to <code>chrome://extensions/</code>, remove the development version, click "Load unpacked", and select <code>build/chrome-mv3-prod</code>. Test thoroughly before publishing.</p>
<p>The extension is lightweight (under 100 KB), only runs when you click the button, and has no background processes when idle.</p>
<h2 id="heading-next-steps-and-extension-ideas">Next Steps and Extension Ideas</h2>
<p>Congratulations on building your first Chrome extension!</p>
<p>You now have a working tool that groups tabs by domain with one click, shows live statistics about open tabs and groups, and is built on modern tooling: TypeScript, React, and Plasmo following Chrome extension best practices.</p>
<p>The extension is a solid foundation. Here are some ideas for where to take it next.</p>
<h3 id="heading-1-auto-grouping">1. Auto-Grouping</h3>
<p>Instead of requiring a button click, you could automatically group new tabs as they're opened. You'd listen for the <code>chrome.tabs.onCreated</code> event in <code>background.ts</code> and trigger <code>groupTabsByDomain()</code> with a short delay to let the page URL load:</p>
<pre><code class="language-typescript">// In background.ts
chrome.tabs.onCreated.addListener(async (tab) =&gt; {
  // Wait a bit for the URL to load
  setTimeout(() =&gt; {
    groupTabsByDomain()
  }, 2000)
})
</code></pre>
<p>This gets into event listeners, asynchronous timing, and thinking carefully about when to fire — a good next step for understanding how background scripts can be more proactive.</p>
<h3 id="heading-2-keyboard-shortcuts">2. Keyboard Shortcuts</h3>
<p>You can trigger grouping without even opening the popup by adding a keyboard shortcut. Add a <code>commands</code> section to the manifest in <code>package.json</code>:</p>
<pre><code class="language-json">"manifest": {
  "commands": {
    "group-tabs": {
      "suggested_key": {
        "default": "Ctrl+Shift+G",
        "mac": "Command+Shift+G"
      },
      "description": "Group tabs by domain"
    }
  }
}
</code></pre>
<p>Then listen for the command in <code>background.ts</code>:</p>
<pre><code class="language-typescript">chrome.commands.onCommand.addListener((command) =&gt; {
  if (command === "group-tabs") {
    groupTabsByDomain()
  }
})
</code></pre>
<h3 id="heading-3-category-based-grouping">3. Category-Based Grouping</h3>
<p>Rather than grouping by raw domain, you could group by category — putting GitHub, Stack Overflow, and npm together in a "Dev" group, for instance:</p>
<pre><code class="language-typescript">const categories = {
  social: ["facebook.com", "twitter.com", "instagram.com"],
  shopping: ["amazon.com", "ebay.com", "etsy.com"],
  dev: ["github.com", "stackoverflow.com", "npmjs.com"]
}

function getCategoryForDomain(domain: string): string {
  for (const [category, domains] of Object.entries(categories)) {
    if (domains.includes(domain)) {
      return category
    }
  }
  return "other"
}
</code></pre>
<h3 id="heading-4-options-page">4. Options Page</h3>
<p>Plasmo makes it trivial to add a settings page by creating an <code>options.tsx</code> file.</p>
<p>This is where you'd let users toggle auto-grouping, choose between domain and category mode, or configure their own category mappings.</p>
<p>It's a good introduction to the Chrome Storage API and persisting user preferences.</p>
<pre><code class="language-tsx">function OptionsPage() {
  return (
    &lt;div&gt;
      &lt;h1&gt;Tab Grouper Settings&lt;/h1&gt;
      &lt;label&gt;
        &lt;input type="checkbox" /&gt;
        Enable auto-grouping
      &lt;/label&gt;
      &lt;label&gt;
        &lt;input type="checkbox" /&gt;
        Group by category instead of domain
      &lt;/label&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<h3 id="heading-5-tab-age-tracking">5. Tab Age Tracking</h3>
<p>You could track when each tab was created and surface tabs that have been sitting untouched for a week or more, a nice way to encourage tab hygiene:</p>
<pre><code class="language-typescript">// Track tab creation times
const tabCreationTimes = new Map&lt;number, number&gt;()

chrome.tabs.onCreated.addListener((tab) =&gt; {
  if (tab.id) {
    tabCreationTimes.set(tab.id, Date.now())
  }
})

// Find old tabs (e.g., &gt; 7 days)
function getOldTabs(): chrome.tabs.Tab[] {
  const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000)
  return tabs.filter(tab =&gt; {
    const created = tabCreationTimes.get(tab.id!)
    return created &amp;&amp; created &lt; sevenDaysAgo
  })
}
</code></pre>
<h3 id="heading-6-search-within-groups">6. Search Within Groups</h3>
<p>A search bar in the popup would let users filter their open tabs by title, making it easy to jump to a specific tab:</p>
<pre><code class="language-tsx">const [searchQuery, setSearchQuery] = useState("")

const filteredTabs = tabs.filter(tab =&gt;
  tab.title?.toLowerCase().includes(searchQuery.toLowerCase())
)
</code></pre>
<h3 id="heading-7-exportimport-groups">7. Export/Import Groups</h3>
<p>You could let users save their current tab groups to a JSON file and restore them later. Useful for preserving a working session across restarts:</p>
<pre><code class="language-typescript">// Export
async function exportGroups() {
  const groups = await chrome.tabGroups.query({})
  const data = JSON.stringify(groups)
  const blob = new Blob([data], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  chrome.downloads.download({ url, filename: 'tab-groups.json' })
}

// Import
async function importGroups(file: File) {
  const text = await file.text()
  const groups = JSON.parse(text)
  // Restore groups...
}
</code></pre>
<h3 id="heading-8-group-statistics-dashboard">8. Group Statistics Dashboard</h3>
<p>An expanded popup could show browsing analytics, total tabs opened today, most-visited domain, and more:</p>
<pre><code class="language-tsx">function Statistics() {
  const [stats, setStats] = useState({
    totalTabs: 0,
    totalGroups: 0,
    mostUsedDomain: "",
    tabsToday: 0
  })

  return (
    &lt;div&gt;
      &lt;h3&gt;Browsing Statistics&lt;/h3&gt;
      &lt;p&gt;Total tabs opened today: {stats.tabsToday}&lt;/p&gt;
      &lt;p&gt;Most visited domain: {stats.mostUsedDomain}&lt;/p&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<h2 id="heading-learning-resources">Learning Resources</h2>
<p>If you want to go deeper, the <a href="https://developer.chrome.com/docs/extensions/">official Chrome Extension docs</a> are excellent and cover every API in detail.</p>
<p>The <a href="https://github.com/GoogleChrome/chrome-extensions-samples">Chrome Extension Samples repository</a> on GitHub has dozens of real examples to learn from. For Plasmo-specific questions, the <a href="https://docs.plasmo.com/">Plasmo documentation</a> and <a href="https://github.com/PlasmoHQ/examples">example repository</a> are the best starting points, and the community is active on <a href="https://www.plasmo.com/community">Plasmo Discord</a>.</p>
<p>The <a href="https://react.dev/">React docs</a> and <a href="https://www.typescriptlang.org/docs/">TypeScript docs</a> are worth bookmarking as reference material, and the <a href="https://react-typescript-cheatsheet.netlify.app/">React TypeScript Cheatsheet</a> is handy when you're unsure about specific type patterns.</p>
<p>For community support, Stack Overflow's <code>chrome-extension</code> tag is well-monitored, and r/chrome_extensions on Reddit is a friendly place to ask questions.</p>
<h2 id="heading-deploying-to-chrome-web-store">Deploying to Chrome Web Store</h2>
<p>Now that you've built and tested your extension, here's how to publish it and share it with the world.</p>
<h3 id="heading-what-youll-need">What You'll Need</h3>
<p>Before you can publish, you'll need a completed and tested extension, a Google account, a $5 USD one-time developer registration fee, and some store assets such as icons, screenshots, and a written description.</p>
<p>The $5 fee is a one-time charge (not annual) that Google uses to verify developer identity and reduce spam. It covers unlimited extension submissions and is processed immediately via Google Payments.</p>
<h3 id="heading-step-1-create-a-production-build">Step 1: Create a Production Build</h3>
<p>Build your extension for production if you didn't do this before:</p>
<pre><code class="language-bash">cd tab-grouper-tutorial
npm run build
</code></pre>
<p>This creates an optimized version in <code>build/chrome-mv3-prod/</code>. The production build minifies JavaScript and CSS for a smaller file size, strips out development-only code and console logs, and optimizes assets for faster loading.</p>
<p>Before uploading, load <code>build/chrome-mv3-prod/</code> as an unpacked extension and test all features one more time to confirm nothing broke in the build process.</p>
<h3 id="heading-step-2-create-store-assets">Step 2: Create Store Assets</h3>
<h4 id="heading-extension-icons">Extension Icons</h4>
<p>You'll need icons in three sizes: <strong>128×128 pixels</strong> for the main store listing (required), <strong>48×48</strong> for the extension management page, and <strong>16×16</strong> for use as a favicon.</p>
<p>All should be PNG files with transparent backgrounds. Keep the design simple and recognizable at small sizes. Avoid putting text in the 16×16 version.</p>
<p><a href="https://figma.com">Figma</a> is free and works well for this, as does <a href="https://canva.com">Canva</a> or <a href="https://gimp.org">GIMP</a>.</p>
<h4 id="heading-screenshots">Screenshots</h4>
<p>Upload between 1 and 5 screenshots at either 1280×800 or 640×400 pixels (PNG or JPEG).</p>
<p>Show the extension in actual use rather than mockups. The popup with statistics, tabs being grouped, and the before/after state all work well.</p>
<p>Adding annotations to highlight key features helps users understand what they're looking at.</p>
<h4 id="heading-promotional-images-optional">Promotional Images (Optional)</h4>
<p>If you want to be featured on the store, you can also upload a small tile (440×280), large tile (920×680), and marquee image (1400×560). These are only needed if Google chooses to promote your extension.</p>
<h4 id="heading-demo-video-optional">Demo Video (Optional)</h4>
<p>A short YouTube video (30–60 seconds) showing the extension in action can significantly increase conversions. Link to it in your store listing.</p>
<h3 id="heading-step-3-write-your-store-listing">Step 3: Write Your Store Listing</h3>
<p><strong>Extension Name</strong> (45 character limit): Be clear and descriptive. "Tab Grouper - Organize Tabs by Domain" works well. Avoid keyword stuffing or excessive punctuation.</p>
<p><strong>Summary</strong> (132 character limit): This is what appears in search results. Lead with what the extension does: "Automatically organize browser tabs by domain. One-click grouping keeps your workspace clean and productive."</p>
<p><strong>Detailed Description</strong> (16,000 character limit): Start with what the extension does, list features clearly, explain how to use it, address privacy, and provide contact information. Here's a template you can adapt:</p>
<pre><code class="language-markdown">## What is Tab Grouper?

Tab Grouper automatically organizes your browser tabs by grouping them based on their website domain. No more hunting through dozens of tabs - everything is neatly organized.

## Features

- ✅ One-click tab grouping
- ✅ Automatic color-coding by domain
- ✅ Real-time statistics
- ✅ Works with all websites
- ✅ Lightweight and fast

## How to Use

1. Click the Tab Grouper icon in your toolbar
2. Click "Group Tabs by Domain"
3. Your tabs are instantly organized

## Why You Need This

If you regularly have numerous tabs open, finding the right one can waste valuable time. Tab Grouper solves this by automatically organizing tabs into colored groups, making navigation quick and straightforward.

## Privacy

This extension does not collect any personal data. It only accesses tab information locally to perform grouping. No data is sent to external servers.

## Support

Found a bug or have a suggestion? Contact us at support@example.com
</code></pre>
<p><strong>Category</strong>: Choose <strong>Productivity</strong> for Tab Grouper. You can add additional languages later if you want to localize the listing.</p>
<h3 id="heading-step-4-register-as-a-chrome-web-store-developer">Step 4: Register as a Chrome Web Store Developer</h3>
<p>Go to the <a href="https://chrome.google.com/webstore/devconsole">Chrome Web Store Developer Dashboard</a>, sign in with your Google account, accept the Developer Agreement, and pay the $5 registration fee. Your account is activated within minutes.</p>
<h3 id="heading-step-5-submit-your-extension">Step 5: Submit Your Extension</h3>
<p>In the Developer Dashboard, click <strong>"New Item"</strong> and upload your extension. You can either manually zip the <code>build/chrome-mv3-prod/</code> folder or use Plasmo's package command:</p>
<pre><code class="language-bash"># Option 1: Manual zip
cd build/chrome-mv3-prod
zip -r ../../tab-grouper.zip .

# Option 2: Use Plasmo package command
cd tab-grouper-tutorial
npm run package
</code></pre>
<p>Once uploaded, fill in all four sections of the store listing form: <strong>Product details</strong> (name, summary, description, category, language), <strong>Graphic assets</strong> (icon and screenshots), <strong>Privacy practices</strong> (see below), and <strong>Distribution</strong> (visibility, regions, pricing).</p>
<h4 id="heading-single-purpose-description">Single Purpose Description</h4>
<p>Chrome requires each extension to have a single, clearly stated purpose. For Tab Grouper: "This extension organizes browser tabs by grouping them based on their domain name, helping users manage multiple open tabs efficiently."</p>
<h4 id="heading-permission-justification">Permission Justification</h4>
<p>You'll need to justify each permission you declared. For <code>tabs</code>: "The tabs permission is required to read tab URLs and titles in order to group them by domain." For <code>tabGroups</code>: "The tabGroups permission is required to create and manage tab groups for organization."</p>
<h4 id="heading-privacy-policy">Privacy Policy</h4>
<p>Even though Tab Grouper doesn't collect personal data, Chrome may require a privacy policy. Host one on GitHub Pages or your personal website and link to it. Here's a minimal template:</p>
<pre><code class="language-markdown"># Privacy Policy for Tab Grouper

## Data Collection
Tab Grouper does not collect, store, or transmit any personal data.

## Permissions
- **tabs**: Used only to read tab URLs for grouping purposes
- **tabGroups**: Used only to create and manage tab groups

## Local Processing
All tab grouping happens locally in your browser. No data is sent to external servers.

## Contact
For questions: your-email@example.com

Last updated: [Current Date]
</code></pre>
<h3 id="heading-step-6-submit-for-review">Step 6: Submit for Review</h3>
<p>Before clicking submit, run through this checklist:</p>
<ul>
<li><p>Production build tested thoroughly</p>
</li>
<li><p>All store assets uploaded (icon + at least one screenshot)</p>
</li>
<li><p>Description is clear and accurate</p>
</li>
<li><p>Permissions are justified</p>
</li>
<li><p>Privacy policy is linked</p>
</li>
<li><p>Extension name is descriptive</p>
</li>
</ul>
<p>When you're ready, click <strong>"Submit for review"</strong>, confirm your details, and click <strong>"Publish"</strong>. Your extension enters the review queue.</p>
<h3 id="heading-step-7-the-review-process">Step 7: The Review Process</h3>
<p>Google typically reviews extensions within 1–3 business days for straightforward submissions, though complex extensions or first submissions can take up to a week. Reviewers check that the extension works as described, that permissions are justified, that there's no malicious code, and that the listing complies with Chrome Web Store policies.</p>
<p>You can track your status in the Developer Dashboard: Pending review → In review → Approved or Rejected. If rejected, Google will email you specific reasons and instructions for resubmitting.</p>
<p>The most common rejection reasons are insufficient permission justification, misleading descriptions, missing privacy policies, and requesting more permissions than necessary. Address each point in the rejection email, update your submission, and resubmit.</p>
<h3 id="heading-step-8-after-approval">Step 8: After Approval</h3>
<p>Once approved, your extension is live at <code>https://chrome.google.com/webstore/detail/[extension-id]</code>. Share the link on social media, write a blog post, post to Reddit (r/chrome, r/chrome_extensions), or submit to Product Hunt to drive installs.</p>
<p>The Developer Dashboard gives you ongoing analytics — total and weekly installs, reviews and ratings, impressions, and uninstall counts. Check it regularly, especially in the first week. Respond to reviews (particularly negative ones), thank users for positive feedback, and use reported bugs to prioritize future updates.</p>
<h3 id="heading-step-9-publishing-updates">Step 9: Publishing Updates</h3>
<p>When you fix bugs or add features, bump the version number in <code>package.json</code> (following <a href="https://semver.org/">Semantic Versioning</a> — patch for bug fixes, minor for new features, major for breaking changes), run <code>npm run build</code>, and upload the new package through the Developer Dashboard's <strong>Package</strong> tab. Updates are typically reviewed faster than initial submissions, often within 24 hours.</p>
<h3 id="heading-step-10-managing-your-extension-long-term">Step 10: Managing Your Extension Long-Term</h3>
<p>The Chrome Web Store provides built-in analytics, but you can also add Google Analytics if you need more detail.</p>
<p>For user support, an email address in the description or a GitHub issues page both work well. As you add features, keep the description updated and maintain a changelog so users know what changed and when. Responding to user questions and reviews goes a long way toward building a loyal base of users who'll recommend the extension to others.</p>
<h3 id="heading-troubleshooting-common-publishing-issues">Troubleshooting Common Publishing Issues</h3>
<p><strong>"Package is invalid" on upload</strong>: Make sure you zipped the contents of <code>build/chrome-mv3-prod/</code> rather than the folder itself, and verify the generated <code>manifest.json</code> is valid JSON.</p>
<p><strong>Rejection: Permissions Not Justified</strong>: In the "Permission justification" field, be specific about which feature requires each permission and what would break without it.</p>
<p><strong>Rejection: Single Purpose Unclear</strong>: Rewrite the single purpose description to focus on one main function, stated plainly.</p>
<p><strong>Low installation rate after launch</strong>: Poor screenshots are often the culprit — they're the first thing most users look at. Make sure they clearly show the extension solving a real problem. Building even a small number of early reviews also makes a big difference to new visitors.</p>
<h3 id="heading-alternative-distribution">Alternative Distribution</h3>
<p>The Chrome Web Store is the right choice for most public extensions. If you're building an internal tool, an <strong>Unlisted</strong> extension (accessible only via direct link, not searchable) is a good option.</p>
<p>If you need to restrict it to users in a specific Google Workspace organization, a <strong>Private</strong> extension is available for that. Self-hosting and sideloading is possible but requires users to enable Developer Mode manually, so it's only practical for very technical audiences.</p>
<h2 id="heading-congratulations">Congratulations!</h2>
<p>You've gone from an empty folder to a live Chrome extension on the Web Store. Along the way you learned how extensions are structured, how background scripts and popups communicate, how Chrome's tab APIs work, and how to navigate the publishing process end to end.</p>
<p>More than any specific API or configuration detail, the most important thing you've built is a mental model for how extensions work and that transfers directly to any extension idea you want to build next.</p>
<p>Keep building, keep learning, and keep shipping!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automate Documentation Conversion with Pandoc in CI/CD Pipelines ]]>
                </title>
                <description>
                    <![CDATA[ In any software project, documentation plays a crucial role in guiding developers, users, and stakeholders through the project's features and functionalities. As projects grow and evolve, managing documentation across various formats—whether it’s mar... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines/</link>
                <guid isPermaLink="false">671949e0521b33716a071ccc</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ci-cd ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Preston Mayieka ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 19:09:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729707865753/d1703b32-ccb4-4cd1-9c3e-3e66fef7e02f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In any software project, documentation plays a crucial role in guiding developers, users, and stakeholders through the project's features and functionalities.</p>
<p>As projects grow and evolve, managing documentation across various formats—whether it’s markdown, HTML, or PDF for offline use—can become a time-consuming and error-prone task.</p>
<p><a target="_blank" href="https://pandoc.org/"><strong>Pandoc</strong></a> is a powerful tool that allows you to convert documentation between formats seamlessly.</p>
<p>Still, even with Pandoc, manually converting files for every update can become a bottleneck in large projects or teams where documentation is frequently updated.</p>
<p>In this article, I’ll guide you through setting up shell scripts, using Makefiles, and integrating Pandoc into CI/CD pipelines to streamline your workflow and keep your documentation up-to-date with minimal effort.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#Why-Automate-Documentation-Conversion">Why Automate Documentation Conversion</a>?</p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-automate-pandoc-using-shell-scripts">How to Automate Pandoc using Shell Scripts</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#basic-shell-script-for-pandoc-conversion">Basic Shell Script for Pandoc Conversion</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#customizing-the-script-for-several-files">Customizing the Script for Several Files</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#automating-with-makefiles">Automating with Makefiles</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#a-makefile-for-pandoc-conversions">A Makefile for Pandoc Conversions</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#defining-dependencies-in-makefiles">Defining Dependencies in Makefiles</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#why-use-makefiles-for-pandoc-automation">Why Use Makefiles for Pandoc Automation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-pandoc-with-ci-cd-pipelines">How to Integrate Pandoc with CI/CD Pipelines</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#example-setting-up-pandoc-with-github-actions">Example: Setting Up Pandoc with GitHub Actions</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#triggering-on-documentation-updates">Triggering on Documentation Updates</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#adapting-for-gitlab-ci-or-jenkins">Adapting for GitLab CI or Jenkins</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#advanced-automation-techniques">Advanced Automation Techniques</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#automating-with-cron-jobs">Automating with Cron Jobs</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#ensuring-consistency-with-docker">Ensuring Consistency with Docker</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#combining-tools-for-efficiency">Combining Tools for Efficiency</a></p>
</li>
<li><p><a target="_blank" href="https://freecodecamp.org/news/how-to-automate-documentation-conversion-with-pandoc-in-cicd-pipelines#conclusion">Conclusion</a></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729625689479/bfba6404-0a0c-4dab-9a15-0058b33c3c0c.png" alt="bfba6404-0a0c-4dab-9a15-0058b33c3c0c" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-why-automate-documentation-conversion">Why Automate Documentation Conversion?</h2>
<p>Manually converting documentation between formats in large projects can become a daunting task.</p>
<p>Whether you're generating <strong>HTML</strong>, <strong>PDF</strong>, or <strong>DOCX</strong> versions from the same source, repeating this process for every update leads to various challenges:</p>
<ul>
<li><p><strong>Time-Consuming</strong>: Running manual commands to convert documentation every time you make a change eats into valuable development time when updates happen often.</p>
</li>
<li><p><strong>Prone to Errors</strong>: The manual process increases the likelihood of mistakes, such as using incorrect commands, missing steps, or generating outdated versions of your documentation. These inconsistencies can confuse both developers and end-users.</p>
</li>
<li><p><strong>Difficult to Scale</strong>: As projects grow in size, managing documents across different formats without automation can become unmanageable. Teams working in parallel may struggle to keep documentation synchronized, leading to mismatches between formats.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729627548454/96b38163-5eda-4412-83fb-483180410c61.png" alt="96b38163-5eda-4412-83fb-483180410c61" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>By automating the conversion process with tools like Pandoc, you can overcome these challenges and enjoy a range of benefits:</p>
<ul>
<li><p><strong>Consistency</strong>: Automation ensures that all versions of your documentation are always up-to-date and accurate. No matter the number of formats you're generating, the process remains standardized.</p>
</li>
<li><p><strong>Efficiency</strong>: Automated workflows free up time by handling repetitive tasks in the background, allowing teams to focus on development rather than manually managing documentation updates.</p>
</li>
<li><p><strong>Scalability</strong>: With automation, it’s straightforward to scale documentation efforts as your project grows. Whether you're maintaining a single document or an entire library of resources, automation makes sure everything stays synchronized with minimal effort.</p>
</li>
</ul>
<p>The next section explores how to automate the conversion process.</p>
<h2 id="heading-how-to-automate-pandoc-using-shell-scripts">How to Automate Pandoc using Shell Scripts</h2>
<p>By using <a target="_blank" href="https://medium.com/@jadhav.swatissj99/introduction-to-shell-scripting-automate-your-workflow-efficiently-d9415537e990">shell scripts</a>, you can streamline the process of running <a target="_blank" href="https://pandoc.org/getting-started.html#step-6-converting-a-file">PanDoc commands</a>, saving time and reducing the risk of errors associated with manual command execution.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729627613042/5a2b2c62-3f67-42a5-81fe-5d4ff58714c3.png" alt="5a2b2c62-3f67-42a5-81fe-5d4ff58714c3" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-basic-shell-script-for-pandoc-conversion">Basic Shell Script for PanDoc Conversion</h3>
<p>To get started, we’ll create a shell script that converts a single Markdown file into various formats.</p>
<p>For instance, here’s a script that converts <code>input.md</code> into HTML and PDF:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Convert input.md to HTML and PDF</span>
pandoc input.md -o output.html
pandoc input.md -o output.pdf

<span class="hljs-built_in">echo</span> <span class="hljs-string">"Conversion complete!"</span>
</code></pre>
<p>In the above script:</p>
<ul>
<li><p>The <code>#!/bin/bash</code> line indicates that the script should be run using the Bash shell.</p>
</li>
<li><p>The pandoc commands convert the <code>input.md</code> file into <code>output.html</code> and <code>output.pdf</code>.</p>
</li>
<li><p>The <code>echo</code> command confirms that the conversion process is complete.</p>
</li>
</ul>
<h3 id="heading-customizing-the-script-for-several-files">Customizing the Script for Several Files</h3>
<p>If you want to convert all Markdown files in a directory, you can customize your script to process several files at once.</p>
<p>For instance, here’s a script that converts <code>input.md</code> into <code>HTML</code> and <code>PDF</code>:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Go through all the Markdown files present in the current directory</span>
<span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> *.md; <span class="hljs-keyword">do</span>
  <span class="hljs-comment"># Convert each Markdown file to PDF</span>
  pandoc <span class="hljs-string">"<span class="hljs-variable">$file</span>"</span> -o <span class="hljs-string">"<span class="hljs-variable">${file%.md}</span>.pdf"</span>
<span class="hljs-keyword">done</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"All conversions complete!"</span>
</code></pre>
<p>In this script:</p>
<ul>
<li><p>The <code>for</code> loop iterates over each <code>.md</code> file in the current directory.</p>
</li>
<li><p>The <code>pandoc</code> command converts each Markdown file to a PDF, preserving the original filename but changing the extension to <code>.pdf</code> with the <code>${file%.md}.pdf</code> syntax.</p>
</li>
<li><p>The final <code>echo</code> confirms that all conversions are complete.</p>
</li>
</ul>
<h3 id="heading-adding-error-handling-and-complex-logic">Adding Error Handling and Complex Logic</h3>
<p>To enhance your script's robustness, you can add error handling and extra logic.</p>
<p>For instance, perhaps you want to make sure that Pandoc is installed before proceeding, and handle cases where the input file may be missing.</p>
<p>The script will be as follows:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-comment"># Check if Pandoc is installed</span>
<span class="hljs-keyword">if</span> ! <span class="hljs-built_in">command</span> -v pandoc &amp;&gt; /dev/null; <span class="hljs-keyword">then</span>
  <span class="hljs-built_in">echo</span> <span class="hljs-string">"Pandoc is not installed. Please install it and try again."</span>
  <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># Go through all the Markdown files present in the current directory</span>
<span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> *.md; <span class="hljs-keyword">do</span>
  <span class="hljs-keyword">if</span> [ -e <span class="hljs-string">"<span class="hljs-variable">$file</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-comment"># Convert each Markdown file to PDF</span>
    pandoc <span class="hljs-string">"<span class="hljs-variable">$file</span>"</span> -o <span class="hljs-string">"<span class="hljs-variable">${file%.md}</span>.pdf"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Converted <span class="hljs-variable">$file</span> to PDF."</span>
  <span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"No Markdown files found."</span>
  <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"All conversions complete!"</span>
</code></pre>
<p>In this enhanced script:</p>
<ul>
<li><p>The <code>if ! command -v pandoc &amp;&gt; /dev/null; then</code> line checks whether Pandoc is installed.</p>
</li>
<li><p>The <code>if [ -e "$file" ]; then</code> statement checks if each Markdown file exists before attempting to convert it.</p>
</li>
<li><p>An informative message is printed after each successful conversion, providing feedback on the process.</p>
</li>
</ul>
<p>As your project grows in complexity, relying solely on shell scripts for automation can become difficult to manage when dealing with lots of files and frequent updates.</p>
<p>This is where <strong>Makefiles</strong> come in handy.</p>
<h3 id="heading-automating-with-makefiles">Automating with Makefiles</h3>
<p>A <a target="_blank" href="https://opensource.com/article/18/8/what-how-makefile">Makefile</a> is a special file that defines rules and commands for automating tasks in a project.</p>
<p>It’s widely used by developers to <strong>compile code</strong>, but developers can also leverage it for non-compilation tasks, such as converting documentation formats with Pandoc.</p>
<h3 id="heading-a-makefile-for-pandoc-conversions">A Makefile for Pandoc Conversions</h3>
<p>Here’s an example of how you can create a Makefile to automate Pandoc conversions:</p>
<pre><code class="lang-makefile"><span class="hljs-section">all: html pdf</span>

<span class="hljs-section">html:</span>
    pandoc input.md -o output.html

<span class="hljs-section">pdf:</span>
    pandoc input.md -o output.pdf
</code></pre>
<p>In this Makefile:</p>
<ul>
<li><p><code>all</code> is the default target. When you run <code>make</code> without specifying a target, it will run the <code>html</code> and <code>pdf</code> targets sequentially.</p>
</li>
<li><p>The <code>html</code> target runs a PanDoc command to convert <a target="_blank" href="http://input.md"><code>input.md</code></a> into <code>output.html</code>.</p>
</li>
<li><p>The <code>pdf</code> target runs a PanDoc command to convert <a target="_blank" href="http://input.md"><code>input.md</code></a> into <code>output.pdf</code>.</p>
</li>
</ul>
<p>To use this Makefile, run the following command in your terminal:</p>
<pre><code class="lang-bash">codemake
</code></pre>
<p>This will execute both the <code>html</code> and <code>pdf</code> targets, converting the Markdown file into both formats.</p>
<h3 id="heading-defining-dependencies-in-makefiles">Defining Dependencies in Makefiles</h3>
<p>One of the major strengths of Makefiles is their ability to handle <strong>dependencies</strong>.</p>
<p>In the context of documentation conversion, you can specify which files to update when you <strong>detect changes</strong> in the source file.</p>
<p>Let’s look at an example:</p>
<pre><code class="lang-makefile"><span class="hljs-section">output.html: input.md</span>
    pandoc input.md -o output.html

<span class="hljs-section">output.pdf: input.md</span>
    pandoc input.md -o output.pdf
</code></pre>
<p>In this Makefile:</p>
<ul>
<li><p>The <code>output.html</code> and <code>output.pdf</code> files depend on <code>input.md</code>.</p>
</li>
<li><p>If you run make output.html or make output.pdf, Pandoc will regenerate the corresponding file if you update <code>input.md</code> after the last time you created the output file.</p>
</li>
</ul>
<p>This ensures that Pandoc converts the files that have changed, saving time when working on large documentation projects.</p>
<h3 id="heading-why-use-makefiles-for-pandoc-automation">Why Use Makefiles for Pandoc Automation?</h3>
<p>Makefiles offer several advantages for automating Pandoc conversions in larger projects:</p>
<ul>
<li><p><strong>Efficiency</strong>: Makefiles rebuild what’s necessary, meaning you won’t waste time converting files that haven’t changed.</p>
</li>
<li><p><strong>Simplicity</strong>: Once set up, running <code>make</code> simplifies the conversion process to a single command, making it easier for teams to maintain consistent documentation workflows.</p>
</li>
<li><p><strong>Scalability</strong>: As your project grows, you can add more targets and dependencies to the Makefile, automating everything from documentation to more complex build processes.</p>
</li>
</ul>
<p>Makefiles are an excellent option for automating documentation conversions, when combined with Pandoc for multi-format outputs.</p>
<p>They help ensure that your workflow is <strong>efficient</strong> and your documentation remains up-to-date.</p>
<p>As modern development practices increasingly rely on <a target="_blank" href="https://www.redhat.com/en/topics/devops/what-is-ci-cd#:~:text=CI%2FCD%2C%20which%20stands%20for,a%20shared%20source%20code%20repository.">Continuous Integration and Continuous Deployment (CI/CD)</a>, automating routine tasks such as documentation generation becomes essential.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729626428543/8194dc35-8a1a-4d91-b4b0-8b8ce7174eba.jpeg" alt="8194dc35-8a1a-4d91-b4b0-8b8ce7174eba" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Integrating Pandoc into your <a target="_blank" href="https://www.redhat.com/en/topics/devops/what-cicd-pipeline">CI/CD pipelines</a> allows for seamless documentation conversion and updates, ensuring that your docs are always up-to-date without manual intervention.</p>
<p>By automating this process, you can focus on coding and building, while your pipeline handles the conversion and distribution of your project documentation. Let’s explore how to set up Pandoc in a CI/CD pipeline to streamline your documentation workflows.</p>
<h2 id="heading-how-to-integrate-pandoc-with-cicd-pipelines">How to Integrate Pandoc with CI/CD Pipelines</h2>
<p>Automating documentation generation within your CI/CD pipeline brings significant benefits, including consistency, efficiency, and hands-off updates.</p>
<p>Whether you’re using <a target="_blank" href="https://github.com/features/actions">GitHub Actions</a>, <a target="_blank" href="https://about.gitlab.com/topics/ci-cd/">GitLab CI</a>, <a target="_blank" href="https://www.jenkins.io/">Jenkins</a>, or another automation tool, integrating Pandoc ensures that documentation gets generated and distributed whenever changes occur.</p>
<p>This approach reduces the risk of <strong>outdated</strong> or <strong>inconsistent</strong> documentation.</p>
<h3 id="heading-example-setting-up-pandoc-with-github-actions">Example: Setting Up Pandoc with GitHub Actions</h3>
<p>Let’s walk through a clear example of how you can use <strong>GitHub Actions</strong> to automate the process of generating documentation with Pandoc.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729626613665/ddc1ddd8-2049-4971-8883-1f86bd80a558.png" alt="ddc1ddd8-2049-4971-8883-1f86bd80a558" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Below is a basic workflow that converts a <strong>Markdown file (*.md)</strong> into a <strong>PDF</strong> whenever code gets pushed to the repository.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Generate</span> <span class="hljs-string">Documentation</span>

<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Step 1: Checkout code from the repository</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-comment"># Step 2: Install Pandoc</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">Pandoc</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">sudo</span> <span class="hljs-string">apt-get</span> <span class="hljs-string">install</span> <span class="hljs-string">pandoc</span>

      <span class="hljs-comment"># Step 3: Convert Markdown to PDF</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Convert</span> <span class="hljs-string">Markdown</span> <span class="hljs-string">to</span> <span class="hljs-string">PDF</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">pandoc</span> <span class="hljs-string">input.md</span> <span class="hljs-string">-o</span> <span class="hljs-string">output.pdf</span>

      <span class="hljs-comment"># Step 4: Upload the generated PDF as an artifact</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">Documentation</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">documentation</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">output.pdf</span>
</code></pre>
<p>Here’s a breakdown of each step:</p>
<ul>
<li><p><strong>Checkout Code</strong>: The <code>actions/checkout@v2</code> action retrieves the latest code from your repository.</p>
</li>
<li><p><strong>Install Pandoc</strong>: This step installs Pandoc on the runner (in this case, an Ubuntu machine) so that it can gets used for the documentation conversion.</p>
</li>
<li><p><strong>Convert Markdown to PDF</strong>: The PanDoc command converts the <a target="_blank" href="http://input.md"><code>input.md</code></a> file into <code>output.pdf</code>.</p>
</li>
<li><p><strong>Upload Documentation</strong>: This step saves the generated PDF as an artifact in the CI/CD pipeline, making it downloadable or accessible later</p>
</li>
</ul>
<h3 id="heading-triggering-on-documentation-updates">Triggering on Documentation Updates</h3>
<p>You can configure your CI/CD pipeline to trigger when documentation updates occur, for example, when changes get pushed to a <code>docs</code> branch:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">docs</span>
</code></pre>
<p>This setup ensures that your documentation gets regenerated when changes are specifically made to the documentation branch, reducing unnecessary rebuilds.</p>
<h3 id="heading-adapting-for-gitlab-ci-or-jenkins">Adapting for GitLab CI or Jenkins</h3>
<p>While the above example uses GitHub Actions, the same principles work on other CI/CD systems like <strong>GitLab CI</strong> or <strong>Jenkins</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729626674435/91e102a9-cb70-4819-bb0d-5a60f16dcdae.jpeg" alt="91e102a9-cb70-4819-bb0d-5a60f16dcdae" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>For instance, in GitLab CI, you could define a <code>.gitlab-ci.yml</code> file that follows similar steps to check out the code, install Pandoc, convert the files, and store the resulting documentation.</p>
<p>Integrating Pandoc into your CI/CD pipeline offers a reliable way to automate your documentation workflows, ensuring that your project docs are always current, accurate, and accessible.</p>
<h2 id="heading-advanced-automation-techniques">Advanced Automation Techniques</h2>
<p>When your project reaches a stage where documentation needs to be generated frequently or across multiple environments, it’s essential to extend your automation strategy to maintain consistency and reliability.</p>
<h3 id="heading-automating-with-cron-jobs">Automating with Cron Jobs</h3>
<p>For projects where documentation updates occur frequently, but not always due to code pushes or commits, you can automate the process using <a target="_blank" href="https://en.wikipedia.org/wiki/Cron"><strong>cron jobs</strong></a>.</p>
<p>Cron allows you to <strong>schedule tasks</strong> at specific intervals, meaning your documentation can automatically get updated at set times without needing manual input.</p>
<p>For example, setting up a cron job to run every day at midnight to convert Markdown files to PDFs:</p>
<pre><code class="lang-bash">0 0 * * * /path/to/pandoc /path/to/input.md -o /path/to/output.pdf
</code></pre>
<p>With this cron job in place, Pandoc will automatically convert your Markdown files to PDFs at the specified time, ensuring that your documentation remains up-to-date without manual intervention.</p>
<h3 id="heading-ensuring-consistency-with-docker">Ensuring Consistency with Docker</h3>
<p>When working in a team or across various systems, ensuring that Pandoc runs consistently in different environments can be challenging.</p>
<p><a target="_blank" href="https://www.docker.com/">Docker</a> provides a solution to this by allowing you to package Pandoc and all its dependencies in a container, ensuring that the same setup gets used everywhere.</p>
<p>This is useful in CI/CD pipelines, where different environments (like local machines, staging, and production servers) can have different configurations.</p>
<p>You can use the official <a target="_blank" href="https://hub.docker.com/u/pandoc"><strong>Pandoc Docker image</strong></a> to simplify the setup process:</p>
<pre><code class="lang-bash">docker run --rm -v $(<span class="hljs-built_in">pwd</span>):/data pandoc/core:latest input.md -o output.pdf
</code></pre>
<p>This command runs Pandoc inside a Docker container, using the latest version of Pandoc, and mounts your current directory to the container’s <code>/data</code> directory.</p>
<p>By doing this, you ensure that no matter where the pipeline runs, the conversion will work the same way every time.</p>
<h3 id="heading-combining-tools-for-efficiency">Combining Tools for Efficiency</h3>
<p>By combining tools like <strong>cron jobs</strong> and <strong>Docker</strong>, you can set up an advanced automation pipeline that ensures:</p>
<ul>
<li><p><strong>Scheduled updates</strong>: Cron jobs trigger documentation generation at regular intervals.</p>
</li>
<li><p><strong>Consistency across environments</strong>: Docker ensures that Pandoc and its dependencies are the same on every machine.</p>
</li>
<li><p><strong>Reliability</strong>: Together, these tools help ensure that your documentation is always accurate, no matter where or when it’s generated.</p>
</li>
</ul>
<p>These advanced techniques allow you to further streamline your documentation workflows, improving both efficiency and reliability as your project grows.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Automating documentation conversion with Pandoc not only saves time but also ensures consistency and scalability as your project grows.</p>
<p>Whether you’re managing small or large projects, these techniques enable you to focus on coding and building, knowing that your documentation is automatically up-to-date and ready for distribution.</p>
<p>By embracing automation, you streamline your development process and enhance collaboration across teams, ensuring that your documentation evolves as efficiently as your code.</p>
<p>Now it’s time to apply these tools to your projects and enjoy the benefits of a fully automated documentation pipeline.</p>
<p><strong>Let’s stay in touch:</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.linkedin.com/in/preston-mayieka/">Connect with me on LinkedIn</a>. I post regularly, so following me is a great idea.</p>
</li>
<li><p><a target="_blank" href="https://mobile.x.com/Preston_Mayieka">Follow me on X</a></p>
</li>
</ul>
<p>Feel free to reach out through the channels above if you have any questions. I will be happy to help.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Speedy Web Compiler? SWC Explained With Examples ]]>
                </title>
                <description>
                    <![CDATA[ In the evolving landscape of JavaScript development, the need for efficient and powerful tooling has become increasingly important. Developers rely on tools like compilers and bundlers to transform their code, optimize performance, and ensure compati... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-speedy-web-compiler/</link>
                <guid isPermaLink="false">66d90463e90270a49f64f4c6</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                    <category>
                        <![CDATA[ web performance ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Preston Mayieka ]]>
                </dc:creator>
                <pubDate>Thu, 05 Sep 2024 01:07:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1725290113209/260f00cb-5bfe-4260-8e45-0c61a1897cae.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the evolving landscape of JavaScript development, the need for efficient and powerful tooling has become increasingly important.</p>
<p>Developers rely on tools like <a target="_blank" href="https://en.wikipedia.org/wiki/Compiler"><strong>compilers</strong></a> and <a target="_blank" href="https://www.codejourney.net/what-is-a-javascript-bundler/"><strong>bundlers</strong></a> to transform their code, optimize performance, and ensure compatibility across different environments.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287352931/41e13dc3-100e-4f75-87df-98342df4beb2.png" alt="41e13dc3-100e-4f75-87df-98342df4beb2" class="image--center mx-auto" width="901" height="506" loading="lazy"></p>
<p>These tools are essential for modern JavaScript applications, enabling developers to write clean, maintainable code while leveraging the latest language features.</p>
<p>This article will help you understand what Speedy Web Compiler (SWC) is and how it helps optimize the performance of your web apps.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-speedy-web-compiler">Introduction to Speedy Web Compiler</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-background-of-swc">Background of SWC</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-speedy-web-compiler-works">How Speedy Web Compiler Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-benefits-of-using-speedy-web-compiler">Benefits of Using Speedy Web Compiler</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-speedy-web-compiler">How to Set Up Speedy Web Compiler in Your Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-swc-integration-in-popular-frameworks">SWC Integration in Popular Frameworks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-speedy-web-compiler">What is Speedy Web Compiler?</h2>
<p>First, let me break it down.</p>
<p>SWC stands for Speedy Web Compiler, and when broken down:</p>
<p><strong>Speedy</strong> - This means it's fast! SWC processes and transforms JavaScript code, making it efficient to use in big projects.</p>
<p><strong>Web</strong> - It’s all about web development. It focuses on improving how JavaScript (the language of the web) handles it.</p>
<p><strong>Compiler</strong> - It takes code written in one form and transforms it into another form that can be better understood or used by computers.</p>
<h2 id="heading-background-of-swc">Background of SWC</h2>
<p><a target="_blank" href="https://github.com/kdy1">kdy1</a>, a South Korean developer and maintainer of <a target="_blank" href="https://nextjs.org/">Next.js</a>, created SWC as a faster tool for handling JavaScript code.</p>
<p>The motivation was the need for speed and efficiency, as web projects grow larger and more complex.</p>
<p>With numerous websites and apps depending on JavaScript, SWC helps developers save time and work more efficiently.</p>
<h2 id="heading-how-speedy-web-compiler-works">How Speedy Web Compiler Works</h2>
<p>SWC uses <a target="_blank" href="https://www.rust-lang.org/">Rust</a>, a programming language known for its speed and safety.</p>
<p>SWC works by taking your JavaScript or TypeScript code and transforming it into a version that can run efficiently in various environments.</p>
<p>Understanding how SWC achieves this involves examining its core steps: parsing, transforming, and generating code.</p>
<h3 id="heading-how-does-swc-parse-code">How Does SWC Parse Code?</h3>
<p>The first step in the compilation process is parsing.</p>
<p>Begins by <strong>reading</strong> and analyzing the code to understand its structure.</p>
<p>This is akin to taking a complex sentence and breaking it down into its grammatical components—subject, verb, object, etc.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287153049/b7ec431c-24db-4c7a-9fb0-ef3ce0d92921.png" alt="Illustration of Code parsing into forming abstract syntax tree" class="image--center mx-auto" width="891" height="499" loading="lazy"></p>
<p>In technical terms, SWC converts your code into an <a target="_blank" href="https://en.wikipedia.org/wiki/Abstract_syntax_tree"><strong>Abstract Syntax Tree (AST)</strong>.</a></p>
<p>The AST is a tree-like representation of the source code, where each node in the tree corresponds to a construct occurring in the code, such as expressions, statements, and functions.</p>
<p>This tree structure allows SWC to process and understand the code’s logic in a way that is both efficient and scalable.</p>
<h3 id="heading-how-does-swc-transform-code">How Does SWC Transform Code?</h3>
<p>After creating the AST, SWC moves on to the transformation phase.</p>
<p>This is where the magic happens—SWC applies various optimizations and changes to the code based on the target environment.</p>
<p>For instance, if you're targeting older browsers that don't support modern JavaScript features, SWC will transform your ES6+ code into a backward-compatible version.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287185279/371d2ac2-e919-4f25-8caf-01d4157fd290.png" alt="Yellow gear with two circular arrows surrounding it, accompanied by the text &quot;Transformation of AST&quot; on a dark background." class="image--center mx-auto" width="888" height="498" loading="lazy"></p>
<p>During this phase, SWC also handles TypeScript transformations. It strips away TypeScript-specific syntax, such as types and interfaces, converting the code into pure JavaScript that gets executed by any JavaScript engine.</p>
<p>SWC can apply custom transformations based on plugins or specific configurations, making it highly versatile.</p>
<h3 id="heading-how-does-swc-generate-optimized-code">How Does SWC Generate Optimized Code?</h3>
<p>After the transformations are complete, SWC proceeds to the final step: code generation.</p>
<p>In this step, SWC takes the transformed AST and converts it back into executable code.</p>
<p>In contrast, this process is not just a reversal of the parsing process.</p>
<p>SWC takes special care to generate code that is both functionally correct and optimized for performance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287212996/af724e34-6473-4104-8244-e35c1be6a3c0.png" alt="Yellow line drawing of a coding window with a wrench and gear icon, above text that reads &quot;Generation of Optimized Code&quot; on a black background." class="image--center mx-auto" width="886" height="496" loading="lazy"></p>
<p>For instance, SWC might remove dead code (code that will never be executed) or inline certain functions to reduce overhead.</p>
<p>The goal is to produce code that is as clean and efficient as possible, ensuring that it runs quickly and reliably in production environments.</p>
<h2 id="heading-benefits-of-using-speedy-web-compiler">Benefits of Using Speedy Web Compiler</h2>
<h3 id="heading-performance">Performance</h3>
<p>One of the major benefits of using SWC is its outstanding speed. SWC achieves this exceptional speed by using Rust.</p>
<p>Developers can expect a significant performance improvement when compiling their code for large projects or codebases.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287254910/9c4827d9-81db-488d-bdb8-6b0806ddfd67.png" alt="Illustration of a stopwatch inside a web browser window, with motion lines indicating speed. The text below reads &quot;Faster Loading Times.&quot;" class="image--center mx-auto" width="893" height="498" loading="lazy"></p>
<p>This speed greatly reduces build times, enhancing the efficiency and responsiveness of the development process. It is so fast, you might think it’s late for a meeting!</p>
<h3 id="heading-optimized-output">Optimized Output</h3>
<p>SWC compiles your code and guarantees that the output is highly optimized for performance in production environment by removing dead code, in-lining functions, and reducing the size of the output.</p>
<p>This makes using SWC cost-effective, saving you from extra expenses during production.</p>
<p>The result is a leaner, faster, and more efficient code that can enhance loading times and performance in web applications.</p>
<h3 id="heading-compatibility">Compatibility</h3>
<p>SWC is fully compatible with modern JavaScript libraries and frameworks.</p>
<p>You do not have to worry about using ES6+ or TypeScript. This makes SWC a versatile choice for your projects.</p>
<h2 id="heading-how-to-set-up-speedy-web-compiler">How to Set Up Speedy Web Compiler</h2>
<h3 id="heading-installation">Installation</h3>
<p>To install SWC in your JavaScript or TypeScript project, follow these steps:</p>
<ol>
<li><strong>Initialize your project:</strong> If you haven't already, start by initializing a new project. In your terminal, run:</li>
</ol>
<pre><code class="lang-bash">npm init -y
</code></pre>
<ol start="2">
<li><strong>Install SWC with npm:</strong> Run the following command to download the pre-built binaries:</li>
</ol>
<pre><code class="lang-bash">npm install -D @swc/cli @swc/core
</code></pre>
<ol start="3">
<li><strong>Create a JavaScript file:</strong> Create a <code>src/index.js</code> file with some code:</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> greet = <span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-string">`Hello, <span class="hljs-subst">${name}</span>!`</span>;
};

<span class="hljs-built_in">console</span>.log(greet(<span class="hljs-string">"World"</span>));
</code></pre>
<ol start="4">
<li><strong>Compile with SWC:</strong> Run SWC from the command line to compile your JavaScript file:</li>
</ol>
<pre><code class="lang-bash">npx swc src/index.js -o dist/index.js
</code></pre>
<ol start="5">
<li><strong>Resulting Code:</strong> The resulting JavaScript code in <code>dist/index.js</code> will look like this:</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-meta">"use strict"</span>;
<span class="hljs-keyword">var</span> greet = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, "</span> + name + <span class="hljs-string">"!"</span>;
};
<span class="hljs-built_in">console</span>.log(greet(<span class="hljs-string">"World"</span>));
</code></pre>
<p>This is the transpiled ES5 code produced by SWC, suitable for environments that require backward compatibility with older JavaScript versions.</p>
<h2 id="heading-swc-integration-in-popular-frameworks">SWC Integration in Popular Frameworks</h2>
<p>If you are using Next.js, Deno, Vite, Remix, Parcel, or Turbopack, SWC is already integrated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1725287305286/e0a1e681-4531-402c-ae41-1f8c5f1318c8.png" alt="Logos of various web development frameworks and tools on a black background, which include: Deno, Vite, Next.js, Remix, Turbopack, and an open cardboard box." class="image--center mx-auto" width="896" height="498" loading="lazy"></p>
<blockquote>
<p>Notable improvements were made on Next.js, a popular React framework, since version 12 (Source: <a target="_blank" href="https://nextjs.org/blog/next-12">Next.js 12: The SDK for the Web</a>)</p>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In the constantly changing realm of JavaScript development, having the correct tools can make a significant difference.</p>
<p>SWC, the <a target="_blank" href="http://swc.rs">Speedy Web Compiler</a>, distinguishes itself as a strong solution for converting and optimizing JavaScript and TypeScript code.</p>
<p>Its impressive speed, owing to its Rust-based implementation, along with its efficient management of code transformations and optimizations, positions it as a powerful tool for modern web development.</p>
<p>If you would like to stay in touch:</p>
<p><a target="_blank" href="https://www.linkedin.com/in/preston-mayieka/">Connect with me on LinkedIn</a></p>
<p><a target="_blank" href="https://mobile.x.com/Preston_Mayieka">Follow me on X</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
