<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gun</title>
    <description>The latest articles on DEV Community by Gun (@gunkev).</description>
    <link>https://dev.to/gunkev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F663140%2Fa75157c3-9645-440a-a0aa-e5d813c606b1.jpeg</url>
      <title>DEV Community: Gun</title>
      <link>https://dev.to/gunkev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gunkev"/>
    <language>en</language>
    <item>
      <title>Building an SEO competitor-analysis Actor with Crawlee and Apify</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Wed, 24 Jun 2026 10:03:31 +0000</pubDate>
      <link>https://dev.to/gunkev/building-an-seo-competitor-analysis-actor-with-crawlee-and-apify-31ke</link>
      <guid>https://dev.to/gunkev/building-an-seo-competitor-analysis-actor-with-crawlee-and-apify-31ke</guid>
      <description>&lt;p&gt;I manage a client's small blog where I regularly publish comparison articles, the kind that rank for queries like "best project management tools" or "windows OS alternatives." After a while, I noticed a pattern: competitors ranking above me weren't necessarily covering more topics, they were just structuring their content differently. I wanted to understand exactly what they were covering that I wasn't.&lt;/p&gt;

&lt;p&gt;My first instinct was to manually open each page, scan the headings, and take notes. That worked for one or two pages. It didn't scale. So I built a Crawlee Actor that does the comparison for me, extracting heading structures from my page and competitor pages, cleaning and normalizing them, and producing a structured JSON output that shows shared topics, missing coverage, and unique sections.&lt;/p&gt;

&lt;p&gt;This article walks through how I built it, what broke along the way, and how I ended up with something reliable enough to run on a monthly schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v18 or higher&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://docs.apify.com/cli/" rel="noopener noreferrer"&gt;Apify CLI&lt;/a&gt; installed (&lt;code&gt;npm install -g apify-cli&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Basic familiarity with JavaScript and async/await&lt;/li&gt;
&lt;li&gt;An Apify account (free tier works fine)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the Actor does
&lt;/h2&gt;

&lt;p&gt;By the end of this guide, I'll have a Crawlee Actor that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crawls my page and competitor pages targeting a given topic&lt;/li&gt;
&lt;li&gt;Extracts structured headings (H2, H3, H4) from real article content&lt;/li&gt;
&lt;li&gt;Removes noise from widgets, sidebars, and injected elements&lt;/li&gt;
&lt;li&gt;Normalizes headings into comparable values&lt;/li&gt;
&lt;li&gt;Extracts meaningful entities from headings&lt;/li&gt;
&lt;li&gt;Compares my page against competitors&lt;/li&gt;
&lt;li&gt;Returns a structured JSON output with:

&lt;ul&gt;
&lt;li&gt;shared sections&lt;/li&gt;
&lt;li&gt;missing topics&lt;/li&gt;
&lt;li&gt;unique coverage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This output gives me a usable baseline for content gap analysis, without opening a single competitor page manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this problem is harder than it looks
&lt;/h2&gt;

&lt;p&gt;At first, comparing pages sounds straightforward. Extract headings from your page, extract headings from competitor pages, and compare them. In practice, this breaks quickly.&lt;/p&gt;

&lt;p&gt;Real-world pages are not clean. They mix actual content with unrelated elements that interfere with extraction. Some of the most common issues I ran into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Noisy sections: Pages often include sidebars, "related articles", newsletters, and promotional blocks. These elements contain headings that are not part of the core content but still get scraped.&lt;/li&gt;
&lt;li&gt;Inconsistent structure: One page may list tools under H2 headings, while another uses H3 or even plain text. The hierarchy is not reliable across sites.&lt;/li&gt;
&lt;li&gt;Injected or styled content: Some sites inject styles or scripts directly into heading elements. Instead of clean text, I was extracting CSS fragments or broken strings like &lt;code&gt;.css-19a5n3-link{color:#0a0a23}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ambiguous headings: Headings like "Best options for teams" or "Top picks" don't clearly identify what they refer to. They are not usable for comparison without further processing.&lt;/li&gt;
&lt;li&gt;Different wording for the same concept: One page may use "Linux Mint", another "Mint Linux". Without normalization, these appear as different items.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you stop at raw extraction, the output becomes noisy and misleading. The real challenge is not scraping — it is isolating the actual article content, removing irrelevant sections, normalizing headings, extracting meaningful entities, and making the data comparable across pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;I started by creating a new Crawlee project and installing the required dependencies. If you're starting from scratch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx crawlee create seo-content-gap-analyzer
&lt;span class="nb"&gt;cd &lt;/span&gt;seo-content-gap-analyzer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you already have a project, install Crawlee:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;crawlee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this workflow is designed to run as an Actor, I also installed the Apify SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;apify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the main logic lives in a single entry file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all the setup I needed to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the Actor input
&lt;/h2&gt;

&lt;p&gt;The Actor needs two main inputs: my page (the one I want to analyze) and competitor pages (used for comparison). I also included a topic label and a few optional settings. Here's an example input for &lt;strong&gt;Windows OS alternatives&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"comparison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"windows os alternatives"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://your-site.com/windows-os-alternatives"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"competitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/windows-alternatives"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/best-windows-alternatives"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/linux-vs-windows-alternatives"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"maxRequests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Input breakdown&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;type&lt;/strong&gt;: Defines the output mode. Here, I'm running a comparison workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;topic&lt;/strong&gt;: A label describing what the analysis is about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;myPage&lt;/strong&gt;: The page I want to evaluate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;competitors&lt;/strong&gt;: A list of URLs ranking for the same topic. These serve as the comparison baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;maxRequests&lt;/strong&gt; &lt;em&gt;(optional)&lt;/em&gt;: Limits how many pages the crawler processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;debug&lt;/strong&gt; &lt;em&gt;(optional)&lt;/em&gt;: Enables additional logging during execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At runtime, the Actor will crawl my page and each competitor page, extract heading structures from all of them, then compare everything to identify overlaps and gaps. This input structure keeps the workflow flexible and reusable across topics. If you want to add validation or a UI for your inputs, the &lt;a href="https://docs.apify.com/platform/actors/development/actor-definition/input-schema/specification/v1" rel="noopener noreferrer"&gt;Actor input schema specification&lt;/a&gt; covers how to set that up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crawling strategy
&lt;/h2&gt;

&lt;p&gt;For this workflow, I used &lt;a href="https://crawlee.dev/api/cheerio-crawler/class/CheerioCrawler" rel="noopener noreferrer"&gt;CheerioCrawler&lt;/a&gt;. This was a deliberate choice. I'm not interacting with pages, clicking buttons, or handling dynamic user flows. The goal is to extract structured content from article pages as efficiently as possible. CheerioCrawler gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast HTML parsing&lt;/li&gt;
&lt;li&gt;low resource usage&lt;/li&gt;
&lt;li&gt;simple DOM traversal&lt;/li&gt;
&lt;li&gt;enough control to clean and process content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a browser-based crawler like Playwright would increase complexity and cost without adding real value for this use case.&lt;/p&gt;

&lt;p&gt;Working with real-world comparison pages quickly exposed edge cases that don't appear in simple demos. The main challenge was not crawling itself, but handling inconsistent and deeply nested HTML structures. Some pages exposed tools cleanly, while others embedded them inside component-based layouts, mixed with styling layers and editorial content.&lt;/p&gt;

&lt;p&gt;To make the crawler reliable, I focused on three areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text normalization: to remove CSS fragments and presentation artifacts before processing&lt;/li&gt;
&lt;li&gt;Entity filtering: to separate actual tools from scores, features, and editorial sections&lt;/li&gt;
&lt;li&gt;Flexible matching: to handle variations in naming across pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These adjustments didn't change how the crawler works at a high level, but they significantly improved the quality of the extracted data.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I actually need from each page
&lt;/h3&gt;

&lt;p&gt;I'm not scraping the entire page. I only care about the main article content, heading structure (H2, H3, H4), and meaningful text inside those headings. Everything else is noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crawler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CheerioCrawler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;maxRequestsPerCrawl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;requestHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;head &amp;gt; title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metaDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta[name="description"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;link[rel="canonical"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aside&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.sidebar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.related&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.recommend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.newsletter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;removeWidgetBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articleRoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getArticleRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;h2List&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;articleRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ignoredHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;h2List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;h3List&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;articleRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ignoredHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;h2List&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h2List&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;h3List&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;pageType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;myPage&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;competitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;metaDescription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;h2List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;h2Count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h2List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;h3Count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;canonical&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;checkedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;crawler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;myPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;competitorUrls&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the initial extraction looks like for three of the pages I tested:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cen0qhw1iuyt5iehk47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0cen0qhw1iuyt5iehk47.png" alt="Initial scrape result from how2shout.com showing raw heading extraction" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuuh065ue3mg19flbzo9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuuh065ue3mg19flbzo9y.png" alt="Initial scrape result from learn-dev-tools.blog" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd5cc7jedkrpmxi37fwuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd5cc7jedkrpmxi37fwuo.png" alt="Initial scrape result from PCMag showing noisy heading output" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a comparison snapshot
&lt;/h2&gt;

&lt;p&gt;At this point, each page was returning structured headings — useful, but not yet comparable. To get a global view, I introduced a comparison layer that aggregates all pages into a single snapshot. The goal is not to change extraction, but to summarize what appears across pages, what is missing from my page, and what is unique.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;comparison&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added a helper function just below &lt;code&gt;uniqueList()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareHeadings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h4List&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])]&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMeaningfulHeading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalizeComparisonLabel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;looksLikeEntityLabel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;competitorsData&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h3List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;h4List&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])])&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMeaningfulHeading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalizeComparisonLabel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;looksLikeEntityLabel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharedHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;myItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nf"&gt;areHeadingsSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;compItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nf"&gt;areHeadingsSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't change the crawler itself. It adds a snapshot layer on top of the extracted data — one summary that shows how my page compares to the rest. After the crawl finishes, I load the dataset and split results into &lt;code&gt;myPage&lt;/code&gt; and &lt;code&gt;competitor&lt;/code&gt; entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;competitor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I run the comparison and store the snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compareHeadings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Comparison Result:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pushData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;comparison&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;generatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives a first global view of the topic: &lt;code&gt;sharedHeadings&lt;/code&gt; shows overlap between my page and competitors, &lt;code&gt;myPageOnlyHeadings&lt;/code&gt; shows content covered only on my page, and &lt;code&gt;competitorOnlyHeadings&lt;/code&gt; shows content competitors cover that my page doesn't.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjt5lggtfomyhr3bjsg48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjt5lggtfomyhr3bjsg48.png" alt="First comparison summary result showing raw heading overlap before cleanup" width="799" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the crawler on another topic
&lt;/h2&gt;

&lt;p&gt;At this point, the crawler worked for one dataset. The next step was to check whether the logic held when the topic changed. Instead of modifying the code, I only changed the input. This time using real project management pages I already had bookmarked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project management tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.learn-dev-tools.blog/best-legal-project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"competitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.paymoapp.com/blog/project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://project-management.com/top-10-project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://zapier.com/blog/free-project-management-software"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what the individual page extractions looked like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvwg8w9mcpdoi5uu1mhu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvwg8w9mcpdoi5uu1mhu8.png" alt="Scrape result from learn-dev-tools.blog for project management topic" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsav46ly5n1gaf2rjsxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsav46ly5n1gaf2rjsxd.png" alt="Scrape result from paymoapp.com" width="799" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahu5a34kkylioeatcyrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahu5a34kkylioeatcyrb.png" alt="Scrape result from zapier.com blog" width="799" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxg2sotxl1hgh22e3j4p1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxg2sotxl1hgh22e3j4p1.png" alt="Scrape result from project-management.com" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here's the comparison summary for this run — this is the output I actually use to plan my content updates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv65catzmugw8cbjorj73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv65catzmugw8cbjorj73.png" alt="Comparison summary result for project management topic" width="799" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one was more telling. The mainstream tools were well covered on my end — monday.com, ClickUp, Trello, Asana, Wrike, Smartsheet, Basecamp all showed up in the shared list. But the &lt;code&gt;competitorOnlyHeadings&lt;/code&gt; was long. Jira, Airtable, Zoho, Teamwork, Podio, Hive — tools I hadn't touched at all. The &lt;code&gt;myPageOnlyHeadings&lt;/code&gt; also caught something I hadn't noticed: noise entries like "but what are" and "key features" were still leaking through, which meant the entity filtering still needed tuning for this type of content. The legal-specific tools I covered — Clio, Thomson Reuters — didn't appear anywhere in the competitor set, which made sense given the article's focus, but it also explained a big chunk of the gap.&lt;/p&gt;

&lt;p&gt;Inspecting the Zapier page structure revealed exactly the kind of noise problem I mentioned earlier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8r96lq4yocynlp0bnxpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8r96lq4yocynlp0bnxpv.png" alt="Zapier HTML structure showing CSS-polluted heading elements" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Extracting tools from comparison pages feels simple, I expected them to sit cleanly in H2 or H3 tags. But once I tried it across a few sites, that assumption broke. Some pages use H3 lists, others use H2 with long descriptions, and more complex ones don't expose tools as clear headings at all.&lt;/p&gt;

&lt;p&gt;One problem was noise. The same heading level mixed tool names, scores, and editorial sections. If I extracted everything blindly, I ended up treating all of it as the same type of data. Another issue was that the DOM isn't just content, on modern pages, styled components wrap everything, so I started pulling CSS fragments or UI text alongside the actual tool names. The &lt;code&gt;h3List&lt;/code&gt; for Zapier pages was polluted with text like &lt;code&gt;.css-19a5n3-link{...}&lt;/code&gt; followed by the actual entity text.&lt;/p&gt;

&lt;p&gt;And even when headings looked clean, their meaning wasn't consistent. A tool might be in H3 on one page and in H2 on another, while lower levels handled features or pricing. So relying on a fixed heading level worked sometimes, but quietly failed on others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing the extraction
&lt;/h2&gt;

&lt;p&gt;I needed to refine the results to stop getting polluted text like &lt;code&gt;.css-19a5n3-link{...}&lt;/code&gt;. To fix this:&lt;/p&gt;

&lt;p&gt;First, I added a text-cleaning layer to strip CSS fragments and normalize headings before comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cleanExtractedText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;css-&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\s]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(?:\[[^\]]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\])?\{[^&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/@media&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;{&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\{[^&lt;/span&gt;&lt;span class="sr"&gt;}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*:&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;;{}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+;/gi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;00a0/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cleanExtractedText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeForComparison&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cleanExtractedText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;com$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;–—&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;[\.]\s&lt;/span&gt;&lt;span class="sr"&gt;*/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\([^&lt;/span&gt;&lt;span class="sr"&gt;)&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\)\s&lt;/span&gt;&lt;span class="sr"&gt;*$/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^\w\s&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\-\.]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, I tightened entity detection instead of treating every heading as a tool. This removes scores, FAQs, pricing sections, action phrases, and other review scaffolding from the comparison layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isEntityLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;canonicalizeEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entityBlocklist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNumericLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isQuestionLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;startsWithAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDetailLike&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isMeaningfulHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;canonicalizeEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isBoilerplateHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third, I improved matching by canonicalizing entity labels and comparing them more flexibly. This is what merges things like &lt;code&gt;monday work management&lt;/code&gt; into &lt;code&gt;monday.com&lt;/code&gt;, or &lt;code&gt;jira software cloud&lt;/code&gt; into &lt;code&gt;jira&lt;/code&gt;, instead of treating them as separate tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractEntityFromHeading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;canonicalizeEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getComparisonTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;areHeadingsSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;na&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;canonicalizeEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;canonicalizeEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;nb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;na&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aCandidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildHeadingCandidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bCandidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildHeadingCandidates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;aCandidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;bCandidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bCandidates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bCompactSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bCandidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;aCandidates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bCompactSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I slimmed the comparison summary by comparing only cleaned items that survive both &lt;code&gt;isMeaningfulHeading()&lt;/code&gt; and &lt;code&gt;isEntityLike()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareHeadings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getComparisonItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMeaningfulHeading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isEntityLike&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compRaw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;competitorsData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;getComparisonItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isMeaningfulHeading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isEntityLike&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharedHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;myRaw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;compRaw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;areHeadingsSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;compItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;compRaw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;myRaw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;areHeadingsSimilar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;extractLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;compItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sharedHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myPageOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uniqueList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;competitorOnlyHeadings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all three changes, the comparison output looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmkyivalqvpo0d435og2e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmkyivalqvpo0d435og2e.png" alt="Enhanced comparison summary showing clean tool names without noise" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The difference is immediate: most of the noise is gone. The extracted lists now behave like actual entity sets, not raw headings. Shared tools are clean and consistent (&lt;code&gt;clickup&lt;/code&gt;, &lt;code&gt;asana&lt;/code&gt;, &lt;code&gt;wrike&lt;/code&gt;), and both &lt;code&gt;myPageOnlyHeadings&lt;/code&gt; and &lt;code&gt;competitorOnlyHeadings&lt;/code&gt; are dominated by recognizable product names rather than mixed content. There are no scores, no editorial sections, and no CSS fragments.&lt;/p&gt;

&lt;p&gt;The individual blog pages looked clean too, each tool clearly separated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fha6afi854aov0nwzay1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fha6afi854aov0nwzay1k.png" alt="Clean extraction result from paymoapp.com after normalization" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qacec7spf18l7itbzlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0qacec7spf18l7itbzlk.png" alt="Clean extraction result from zapier.com after normalization" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjij2vckugt3kdp01pee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjij2vckugt3kdp01pee.png" alt="Clean extraction result from learn-dev-tools.blog" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foasn7uxnnct55ee1t0zw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foasn7uxnnct55ee1t0zw.png" alt="Clean extraction result from project-management.com" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This output shows a clean separation between informational sections and the actual comparison block. I intentionally keep the full outline for each page instead of over-filtering it. The goal is not just extraction. Each individual page gives me a structural view of how competitors position their content: what sections they introduce, how they group tools, and what supporting topics they emphasize.&lt;/p&gt;

&lt;p&gt;The comparison result serves a different purpose. It compresses everything into a single snapshot: what overlaps, what I'm missing, and what competitors are covering. The two outputs are complementary. The outline helps me understand &lt;em&gt;how&lt;/em&gt; competitors structure their pages. The comparison helps me understand &lt;em&gt;what&lt;/em&gt; I should add or remove.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the Actor on Apify
&lt;/h2&gt;

&lt;p&gt;Once the crawler was working locally, I deployed it so it could run on demand or on a schedule. Since the project already follows the Actor structure, deployment is one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apify push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The push itself went fine. But the first run failed. I checked the logs and saw that my inputs weren't present — which made sense once I thought about it. On the Apify platform, each Actor run gets its own isolated storage: a fresh dataset, key-value store, and request queue managed by the platform. The local &lt;code&gt;INPUT.json&lt;/code&gt; I had been using doesn't carry over after a push. I needed to define the input directly in the Console.&lt;/p&gt;

&lt;p&gt;After a few seconds, the Actor appears in your Apify Console under &lt;strong&gt;Actors&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9egua6bvazchu6suhj8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9egua6bvazchu6suhj8x.png" alt="Apify Console home dashboard showing the deployed Actor" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the Actor and set your input:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcbi1zesgjl3hy0xhfl1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcbi1zesgjl3hy0xhfl1n.png" alt="Apify Actor input configuration page" width="800" height="398"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project management tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"myPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.learn-dev-tools.blog/best-legal-project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"competitors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.paymoapp.com/blog/project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://project-management.com/top-10-project-management-software/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://zapier.com/blog/free-project-management-software"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then click &lt;strong&gt;Save &amp;amp; Start&lt;/strong&gt; to run the Actor. After the run completes, go to the &lt;strong&gt;Output&lt;/strong&gt; tab to preview results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh17q5o22kyx0clrli2g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh17q5o22kyx0clrli2g.png" alt="Actor output tab showing crawl results in table format" width="799" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also open the &lt;strong&gt;Dataset&lt;/strong&gt; to inspect the full JSON output — the &lt;a href="https://docs.apify.com/platform/storage/dataset" rel="noopener noreferrer"&gt;Apify Dataset storage docs&lt;/a&gt; explain how to export it in JSON, CSV, or Excel if you need it elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd7shths9umgo9be949z4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd7shths9umgo9be949z4.png" alt="Actor dataset view showing full JSON comparison output" width="799" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scheduling runs
&lt;/h2&gt;

&lt;p&gt;I do competitor analysis monthly, so I set the Actor to run on a schedule instead of triggering it manually each time. Open your Actor in the Apify Console, click the three dots in the top-right corner, and select &lt;strong&gt;"Schedule Actor"&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frk7zn2coqphorock3lx6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frk7zn2coqphorock3lx6.jpg" alt="Apify Console showing the Schedule Actor option in the dropdown menu" width="798" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then configure the frequency:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffi3ewfdm57k1okcuxn2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffi3ewfdm57k1okcuxn2z.png" alt="Schedule Actor settings showing monthly run configuration" width="715" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Running the crawler on a schedule turns it into a monitoring tool instead of a one-time analysis. Each run gives me an updated snapshot of new tools added by competitors, structural changes in their pages, and gaps that appear over time.&lt;/p&gt;

&lt;p&gt;If you want to make your Actor public and earn from it, follow the &lt;a href="https://docs.apify.com/platform/actors/publishing" rel="noopener noreferrer"&gt;Apify Actor publishing guide&lt;/a&gt; to list it on the &lt;a href="https://apify.com/store" rel="noopener noreferrer"&gt;Apify Store&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing capabilities
&lt;/h2&gt;

&lt;p&gt;The current version is reliable for extracting and comparing tools across different page structures. A few improvements that naturally follow from this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handling dynamic pages: Some comparison pages rely heavily on JavaScript rendering. Switching to PlaywrightCrawler would allow extracting content that isn't present in the initial HTML.&lt;/li&gt;
&lt;li&gt;Improving resilience against structural drift: As sites update their layouts, selectors and assumptions can break. Adding fallback strategies or scoring multiple candidate nodes would make extraction more robust over time.&lt;/li&gt;
&lt;li&gt;Tracking changes over time: Instead of a one-time comparison, storing snapshots and detecting changes across runs would turn this into a proper competitor monitoring tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full code is available in the &lt;a href="https://github.com/Gunkev/seo-competitor-analysis-scraper" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; - the entity detection logic is the part most worth adapting if your content niche differs from comparison articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When I ran this against my Windows OS alternatives article, the output was immediately useful. Several tools my competitors were covering didn't appear anywhere on my page. Pop!OS was one of them — it showed up across multiple competitor pages while being completely absent from mine. I went back, added it, restructured a few sections, and added some supporting content based on what the comparison revealed.&lt;/p&gt;

&lt;p&gt;The key lesson wasn't about scraping. It was about the extraction layer. Raw headings are noisy and misleading. The real work is isolating actual content, stripping presentation artifacts, and making headings comparable across pages with completely different structures. Once that's solid, the comparison itself is straightforward.&lt;/p&gt;

&lt;p&gt;If you want to take it further, the natural next steps are switching to PlaywrightCrawler for JavaScript-heavy pages, adding a scheduled diff to track changes over time, and extending the entity detection logic to cover more content niches. The GitHub repository includes all the supporting functions referenced in this article.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>apify</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Localizing SaaS Applications: What Developers and Writers Get Wrong</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Wed, 10 Jun 2026 14:09:03 +0000</pubDate>
      <link>https://dev.to/gunkev/localizing-saas-applications-what-developers-and-writers-get-wrong-4286</link>
      <guid>https://dev.to/gunkev/localizing-saas-applications-what-developers-and-writers-get-wrong-4286</guid>
      <description>&lt;h2&gt;
  
  
  Localizing SaaS Applications: What Developers and Writers Get Wrong
&lt;/h2&gt;

&lt;p&gt;The demo goes live in Tokyo. Dates are formatted as &lt;code&gt;MM/DD/YYYY&lt;/code&gt;. Currency shows a dollar sign. One nav label reads “Einstellungen,” a German string left over from a previous sprint, jammed against a truncated button that was clearly designed for shorter English text. The Japanese enterprise prospect says nothing. They just close the tab.&lt;/p&gt;

&lt;p&gt;Nobody on the team saw it coming. The engineers shipped clean code, writers produced polished copy, translators did their jobs. And still, the product felt foreign, not because the words were wrong, but because the process was wrong.&lt;/p&gt;

&lt;p&gt;This is what localization failure actually looks like in SaaS: not a dramatic mistranslation scandal, but a quiet accumulation of small decisions made by people who never thought about global users at all. A hardcoded date format here. An untranslatable idiom in the onboarding copy there. A button width that never had room to grow.&lt;/p&gt;

&lt;p&gt;Localizing SaaS applications is one of the most misunderstood disciplines in product development. Most teams treat it as a translation project with a start date and an end date. It is neither. It is an architectural posture, a content discipline, and an ongoing product function, and the mistakes happen long before a single string ever reaches a linguist.&lt;/p&gt;

&lt;p&gt;This article breaks down exactly where developers and writers get it wrong, why it matters more than most teams realize, and what better looks like at every layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, Understand What You Are Actually Doing
&lt;/h2&gt;

&lt;p&gt;Before diagnosing the mistakes, it helps to clarify three terms that get used interchangeably in sprint planning docs and product roadmaps. They are not the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Translation&lt;/strong&gt; converts text from one language to another. It changes words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Localization&lt;/strong&gt; (&lt;code&gt;l10n&lt;/code&gt;) adapts a product for a specific locale. It changes the experience: date formats, currency symbols, cultural references, legal copy, even which colors signal trust or danger in a given market. &lt;a href="https://learn.microsoft.com/en-us/globalization/localization/localization-overview" rel="noopener noreferrer"&gt;Microsoft’s localization guidance&lt;/a&gt; describes localization as the process of adapting a product to meet the language, cultural, and functional expectations of a specific market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internationalization&lt;/strong&gt; (&lt;code&gt;i18n&lt;/code&gt;) is the engineering work done before any translation begins. It designs software so it can support multiple locales without requiring structural code changes for each one. Microsoft defines &lt;a href="https://learn.microsoft.com/en-us/globalization/methodology/software-internationalization" rel="noopener noreferrer"&gt;software internationalization&lt;/a&gt; as the planning and design work that makes software adaptable across languages, regions, and cultures.&lt;/p&gt;

&lt;p&gt;Confusing these three does not create a terminology problem, it creates an execution problem. Teams that skip straight to translation and wonder why scaling to a second language costs three times as much as the first are paying the price for skipping i18n. &lt;a href="https://crowdin.com/blog/saas-localization" rel="noopener noreferrer"&gt;Crowdin estimates&lt;/a&gt; that attempting SaaS localization without a proper i18n foundation can increase costs by 3-5x. Delaying i18n post-launch also creates long-term rework because the foundation always determines the ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Developers Get Wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardcoding User-Facing Strings
&lt;/h3&gt;

&lt;p&gt;This is the most widespread and most expensive technical mistake in localizing SaaS applications. When developers embed text directly in component logic, every string becomes a surgical extraction problem later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;title="Settings"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiply that across a two-year-old codebase with four contributors and you are looking at weeks of archaeology before a translator sees a single word.&lt;/p&gt;

&lt;p&gt;The fix is foundational: externalize all user-facing text into resource files such as &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.po&lt;/code&gt;, or &lt;code&gt;.xliff&lt;/code&gt;. Each string gets a key. Instead of &lt;code&gt;"Welcome back!"&lt;/code&gt; hardcoded in a component, you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth.welcomeBack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component no longer cares what language it renders in. This sounds obvious. It gets skipped constantly, especially on small strings and error messages, which are exactly the strings that erode trust when they appear in English in the middle of a localized UI.&lt;/p&gt;

&lt;p&gt;This is also why standardized localization formats matter. Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/globalization/localization/localization-file-formats" rel="noopener noreferrer"&gt;localization file-format guidance&lt;/a&gt; describes XLIFF as a format for exchanging localizable resources between tools, keeping translatable text separate from source files and allowing translators to work without touching the original code structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  String Concatenation
&lt;/h3&gt;

&lt;p&gt;This one is subtle and brutal. Developers write logic like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You have &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; items in your cart.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works fine in English. It fails in languages where grammatical structure differs from English word order. German, Japanese, Arabic, Russian, and many other languages cannot always preserve meaning when translators receive sentence fragments instead of complete sentences. Concatenated strings hand translators fragments with no linguistic context. They are essentially untranslatable.&lt;/p&gt;

&lt;p&gt;Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/globalization/internationalization/message-formatting" rel="noopener noreferrer"&gt;message-formatting guidance&lt;/a&gt; makes the same point from a localization perspective: translators need enough control over placeholder order and placement because word order changes between languages.&lt;/p&gt;

&lt;p&gt;The correct approach uses ICU MessageFormat or an equivalent, giving translators a complete sentence template with named placeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cartItems"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You have {count, plural, one {# item} other {# items}} in your cart."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also forces confrontation with the second concatenation trap: pluralization. English has two common forms. Other languages have different plural categories. The &lt;a href="https://cldr.unicode.org/index/cldr-spec/plural-rules" rel="noopener noreferrer"&gt;Unicode CLDR plural rules&lt;/a&gt; exist because languages do not share one universal singular/plural structure. ICU’s &lt;a href="https://unicode-org.github.io/icu/userguide/format_parse/messages/" rel="noopener noreferrer"&gt;MessageFormat&lt;/a&gt; uses those plural rules to keep plural logic inside the translatable message instead of scattering it across application code.&lt;/p&gt;

&lt;p&gt;The ICU plural category system gives translators the grammar rules they need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;zero&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;one&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;two&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;few&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;many&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;other&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If pluralization logic is hardcoded for English’s binary singular/plural pattern, other languages break silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ignoring Layout Flexibility
&lt;/h3&gt;

&lt;p&gt;Translated text often expands or contracts, and W3C’s &lt;a href="https://www.w3.org/International/articles/article-text-size.en.html" rel="noopener noreferrer"&gt;text size in translation guidance&lt;/a&gt; shows that shorter English strings can expand significantly when translated into other languages. Arabic and Hebrew also read right-to-left. A UI designed exclusively around English string lengths will break in multiple directions at once.&lt;/p&gt;

&lt;p&gt;Buttons should not have fixed pixel widths. Navigation labels need room to breathe. Containers should be tested with pseudo-localization, a technique where placeholder text mimics the character width and encoding of target languages before real translation begins.&lt;/p&gt;

&lt;p&gt;Catching a layout collapse in pseudo-localization takes minutes. Catching it in production, in a Japanese enterprise account, takes weeks of engineering time and an apology nobody wants to write.&lt;/p&gt;

&lt;p&gt;RTL support deserves its own flag in the i18n roadmap. Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/globalization/methodology/software-internationalization" rel="noopener noreferrer"&gt;software internationalization guidance&lt;/a&gt; explicitly flags bidirectional writing systems, such as Arabic and Hebrew, as something products should account for early.&lt;/p&gt;

&lt;p&gt;CSS logical properties make this significantly less painful when built in from the start rather than retrofitted after the fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;margin-inline-start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;padding-inline-end&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is safer than hardcoding layout rules like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;margin-left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;padding-right&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Treating Locale as a Display-Layer Problem
&lt;/h3&gt;

&lt;p&gt;Locale is not a stylesheet. It affects business logic.&lt;/p&gt;

&lt;p&gt;The date &lt;code&gt;05/06/07&lt;/code&gt; can mean different things depending on the locale. Number separators vary by country. Pricing display norms differ by market. Address field structures differ by country. Phone number formats differ by region.&lt;/p&gt;

&lt;p&gt;None of this is a translation issue. All of it needs to be handled at the data and logic layer, using locale-aware libraries rather than format strings assembled at render time.&lt;/p&gt;

&lt;p&gt;The JavaScript &lt;a href="https://www.w3.org/International/i18n-drafts/articles/intl/index.en" rel="noopener noreferrer"&gt;&lt;code&gt;Intl&lt;/code&gt; API&lt;/a&gt; exists for this reason. It provides locale-sensitive formatting for dates, times, numbers, lists, and currencies, so applications do not have to rely on fragile manual formatting rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Writers and Content Teams Get Wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Writing Source Copy That Cannot Be Localized
&lt;/h3&gt;

&lt;p&gt;Technical writers and UX copywriters set the ceiling for every language that follows. If the English source is idiomatic, assumes Western context, or relies on wordplay, the localized versions will either sound awkward or cost a fortune to adapt properly.&lt;/p&gt;

&lt;p&gt;Phrases like “let’s get the ball rolling,” “out of the box,” or “circle back” are invisible to native English speakers and genuinely difficult to translate for most linguists without losing meaning entirely. The same goes for culturally embedded metaphors, puns in feature names, and humor that depends on English phonetics.&lt;/p&gt;

&lt;p&gt;The fix is straightforward: write source copy with localization in mind from the first draft. Use plain language. Favor direct, complete sentences over headline fragments. Fragments often drop grammatical cues that translators need to determine gender, tense, and case. Avoid idioms unless you are willing to budget for creative adaptation in every target locale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending Strings Without Context
&lt;/h3&gt;

&lt;p&gt;This is one of the most consistently cited mistakes among localization professionals. When a translator receives the string “Charge” with no additional information, they cannot tell if it means a financial transaction, a battery level, or a database write operation. The string “Check-in” could be a button, a status indicator, or a noun phrase.&lt;/p&gt;

&lt;p&gt;Without a screenshot, a usage note, or a character limit, the translator is guessing, and a wrong guess on a billing CTA has direct revenue consequences.&lt;/p&gt;

&lt;p&gt;Every string handed to a translation workflow should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where it appears in the UI&lt;/li&gt;
&lt;li&gt;what action or state it describes&lt;/li&gt;
&lt;li&gt;any character limit constraints&lt;/li&gt;
&lt;li&gt;a screenshot if one exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This also reduces retranslation cost. Microsoft’s &lt;a href="https://learn.microsoft.com/en-us/globalization/localization/localization-overview" rel="noopener noreferrer"&gt;localization overview&lt;/a&gt; notes that source errors, inconsistent terminology, and unclear materials can slow translation and create additional localization work after the source text changes.&lt;/p&gt;

&lt;p&gt;Every major translation management system, including Lokalise, Phrase, and Crowdin, supports contextual metadata. The problem is not tooling availability. It is the discipline of populating that metadata before the handoff, not after the rework comes back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Treating Localization as a One-Time Release Task
&lt;/h3&gt;

&lt;p&gt;SaaS products ship continuously. Features are added weekly. Error messages are revised. Onboarding copy evolves with every A/B test. When localization is treated as a project milestone rather than a continuous workflow, translated versions fall behind the English source almost immediately.&lt;/p&gt;

&lt;p&gt;Users in localized markets end up with a mixed-language interface: their language for established features, English for everything new. This is not a small cosmetic issue. It signals, loudly, that they are second-class users.&lt;/p&gt;

&lt;p&gt;The answer is continuous localization: integrating translation workflows directly into the CI/CD pipeline so that new or updated strings trigger translation automatically when a developer pushes.&lt;/p&gt;

&lt;p&gt;Git integrations in tools like Crowdin, Phrase, and Transifex make this operational rather than aspirational. &lt;a href="https://github.com/crowdin/github-action" rel="noopener noreferrer"&gt;Crowdin’s GitHub Action&lt;/a&gt; can upload source files, download translations, and create pull requests. &lt;a href="https://support.phrase.com/hc/en-us/articles/5784125562012-GitHub-Strings" rel="noopener noreferrer"&gt;Phrase&lt;/a&gt; and &lt;a href="https://help.transifex.com/en/articles/6265125-github-installation-and-configuration" rel="noopener noreferrer"&gt;Transifex&lt;/a&gt; also document GitHub integrations for synchronizing localization files between repositories and translation workflows.&lt;/p&gt;

&lt;p&gt;When the engineering release cycle and the localization cycle run in parallel, you get simultaneous global launches. When they are decoupled, you get a perpetually outdated product for your non-English users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where SEO Enters the Localization Workflow
&lt;/h2&gt;

&lt;p&gt;Localized product content also needs localized search signals. If two pages target different language or regional versions of the same content, the implementation should help search engines understand the relationship between those versions.&lt;/p&gt;

&lt;p&gt;For multilingual SaaS sites, this usually means using separate localized URLs, translated metadata, and correct &lt;code&gt;hreflang&lt;/code&gt; annotations. Google’s documentation explains that &lt;a href="https://developers.google.com/search/docs/specialty/international/localized-versions" rel="noopener noreferrer"&gt;&lt;code&gt;hreflang&lt;/code&gt; helps it understand localized variations of the same page&lt;/a&gt;, so users can be served the version that best matches their language or region.&lt;/p&gt;

&lt;p&gt;This matters because localization does not stop at the interface. Pricing pages, documentation, onboarding flows, help-center articles, and product-led SEO pages all become part of the localized user journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Structural Problem Underneath All of It
&lt;/h2&gt;

&lt;p&gt;Most of the mistakes above share a common origin: localization has no owner until it becomes a crisis.&lt;/p&gt;

&lt;p&gt;In the design sprint, it is nobody’s job to verify that the component supports RTL. In the content review, it is nobody’s job to flag an untranslatable idiom. In the engineering standup, it is nobody’s job to confirm that a new error message has been externalized and keyed.&lt;/p&gt;

&lt;p&gt;So these gaps accumulate quietly, sprint after sprint, until the Tokyo demo goes sideways and suddenly it is everyone’s problem at once.&lt;/p&gt;

&lt;p&gt;The organizations that scale globally without painful rework have done one structural thing differently: they embedded localization ownership into product development before it became urgent. That means a localization lead with a seat at the product table, a shared glossary enforced at the TMS level, and a standing checklist: no new feature ships without an i18n review.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Is a Systems Problem, Not a Language Problem
&lt;/h2&gt;

&lt;p&gt;Localizing SaaS applications looks like a language challenge from the outside. From the inside, it is a systems design challenge with a language-shaped surface.&lt;/p&gt;

&lt;p&gt;The translator cannot fix a hardcoded string. The writer cannot untangle a concatenated sentence after the fact. The product manager cannot retroactively build RTL support into a layout that was never designed for it.&lt;/p&gt;

&lt;p&gt;Every one of these problems is seeded at the source in architecture decisions, content choices, and workflow assumptions made by people who were not thinking about global users at the time.&lt;/p&gt;

&lt;p&gt;The good news is that none of these mistakes requires heroics to prevent. Externalize your strings early. Write source copy in plain, translatable language. Give translators context, not just words. Build localization into your release pipeline rather than scheduling it as a separate sprint. Assign ownership before the crisis arrives.&lt;/p&gt;

&lt;p&gt;Do those things, and the translation itself, the part most people treat as the whole job, becomes the easy part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources and Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/globalization/localization/localization-overview" rel="noopener noreferrer"&gt;Microsoft: Localize your product&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/globalization/methodology/software-internationalization" rel="noopener noreferrer"&gt;Microsoft: Software internationalization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/globalization/localization/localization-file-formats" rel="noopener noreferrer"&gt;Microsoft: Localization file formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/globalization/internationalization/message-formatting" rel="noopener noreferrer"&gt;Microsoft: Message formatting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/International/articles/article-text-size.en.html" rel="noopener noreferrer"&gt;W3C: Text size in translation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/International/i18n-drafts/articles/intl/index.en" rel="noopener noreferrer"&gt;W3C: JavaScript internationalization API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cldr.unicode.org/index/cldr-spec/plural-rules" rel="noopener noreferrer"&gt;Unicode CLDR: Plural rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unicode-org.github.io/icu/userguide/format_parse/messages/" rel="noopener noreferrer"&gt;ICU: MessageFormat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/search/docs/specialty/international/localized-versions" rel="noopener noreferrer"&gt;Google Search Central: Localized versions of pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://crowdin.com/blog/saas-localization" rel="noopener noreferrer"&gt;Crowdin: SaaS localization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/crowdin/github-action" rel="noopener noreferrer"&gt;Crowdin GitHub Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.phrase.com/hc/en-us/articles/5784125562012-GitHub-Strings" rel="noopener noreferrer"&gt;Phrase GitHub integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.transifex.com/en/articles/6265125-github-installation-and-configuration" rel="noopener noreferrer"&gt;Transifex GitHub integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>localization</category>
      <category>internationalization</category>
      <category>i18n</category>
      <category>tms</category>
    </item>
    <item>
      <title>How to Analyze a CSV File with Python and Pandas</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Fri, 15 May 2026 18:17:23 +0000</pubDate>
      <link>https://dev.to/gunkev/how-to-analyze-a-csv-file-with-python-and-pandas-32o5</link>
      <guid>https://dev.to/gunkev/how-to-analyze-a-csv-file-with-python-and-pandas-32o5</guid>
      <description>&lt;p&gt;CSV files show up in almost every business workflow. You may export orders from an ecommerce platform, download leads from a CRM, receive finance data from another team, or pull product usage data from an internal dashboard.&lt;br&gt;
The challenge is that a raw CSV file rarely answers a question directly. It may contain duplicate rows, missing values, inconsistent column names, and dates stored in a format that makes time analysis difficult.&lt;br&gt;
In this tutorial, you will work through a realistic sales analysis project. Imagine you received an ecommerce order export and need to answer one practical question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What can this CSV tell us about revenue by product, category, country, and month?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will start with a raw CSV file and finish with cleaned data, useful summaries, visualizations, and exported result files.&lt;br&gt;
By the end, you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loaded a CSV file into Pandas&lt;/li&gt;
&lt;li&gt;Inspected rows, columns, data types, and missing values&lt;/li&gt;
&lt;li&gt;Cleaned column names, duplicates, missing values, and data types&lt;/li&gt;
&lt;li&gt;Created a revenue column&lt;/li&gt;
&lt;li&gt;Used filtering, sorting, and &lt;code&gt;groupby()&lt;/code&gt; to answer business questions&lt;/li&gt;
&lt;li&gt;Created a category bar chart and monthly line chart&lt;/li&gt;
&lt;li&gt;Exported cleaned data and summary CSV files
## Prerequisites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should know basic Python syntax, including variables, function calls, imports, and running scripts or notebooks. You do not need previous Pandas experience.&lt;br&gt;
You can follow along in Jupyter Notebook, Google Colab, VS Code, or a regular Python script. A notebook gives you the easiest learning experience because you can inspect each result as you go.&lt;/p&gt;

&lt;p&gt;Install Pandas and Matplotlib:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m pip install pandas matplotlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you use Conda, install from &lt;code&gt;conda-forge&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;conda install -c conda-forge pandas matplotlib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now import the libraries:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pandas as pd
import matplotlib.pyplot as plt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Check your Pandas version:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(pd.__version__)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You may see output like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This tutorial uses modern Pandas practices. You will avoid chained assignment, and you will not use &lt;code&gt;inplace=True&lt;/code&gt; as the default way to update data. Direct assignment usually reads better and works well with current Pandas behavior.&lt;/p&gt;

&lt;p&gt;If Python raises &lt;code&gt;ModuleNotFoundError: No module named 'pandas'&lt;/code&gt;, your editor and terminal may use different Python environments. In VS Code, check the selected interpreter. In Jupyter, check the active kernel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the Sales CSV File
&lt;/h2&gt;

&lt;p&gt;You will use one dataset throughout this tutorial: a small ecommerce sales export. Keeping the dataset small lets you see every row, but the workflow matches what you would do with a larger real-world CSV. Either use a sale csv file you have, create a sample one or &lt;a href="https://drive.google.com/file/d/1KcML2xBXG5kBX4yoWRdwI-cs1LHd5vo8/view?usp=sharing" rel="noopener noreferrer"&gt;&lt;strong&gt;use this example&lt;/strong&gt;&lt;/a&gt; to follow along&lt;/p&gt;

&lt;p&gt;The dataset includes the columns you would expect in a sales export: order ID, order date, product, category, quantity, unit price, customer country, and payment method. It also includes a few issues on purpose. One row appears twice, one order has no date, one order has no category, and one order has no unit price.&lt;/p&gt;

&lt;p&gt;Those issues are useful. They let you practice the kind of judgment you need when analyzing real CSV files. &lt;/p&gt;

&lt;h2&gt;
  
  
  Load the CSV File With Pandas
&lt;/h2&gt;

&lt;p&gt;Load the file with &lt;code&gt;pd.read_csv()&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pd.read_csv(
    "sales_data.csv",
    parse_dates=["Order Date"]
)

print(df.head())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b2ccnvfu65p1h5bd46n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b2ccnvfu65p1h5bd46n.png" width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code creates a DataFrame named &lt;code&gt;df&lt;/code&gt;. A DataFrame is Pandas’ table structure: rows represent records, and columns represent fields.&lt;br&gt;
The &lt;code&gt;parse_dates=["Order Date"]&lt;/code&gt; argument matters because you will analyze monthly revenue later. If you load dates as plain text, you create extra work for yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understand File Paths&lt;/strong&gt;&lt;br&gt;
The path &lt;code&gt;"sales_data.csv"&lt;/code&gt; is a relative path. It means Python should look for the file in the folder where your code runs.&lt;br&gt;
If your file lives somewhere else, use an absolute path. On Windows, that might look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pd.read_csv(
    r"C:\Users\YourName\Downloads\sales_data.csv",
    parse_dates=["Order Date"]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For this project, I would encourage you to use relative paths. They make your analysis easier to move between folders, machines, and teammates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful&lt;/strong&gt; &lt;code&gt;**read_csv()**&lt;/code&gt; &lt;strong&gt;Options&lt;/strong&gt;&lt;br&gt;
You do not need many options for this sample file, but real CSV files often need extra instructions.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pd.read_csv(
    "sales_data.csv",
    sep=",",
    encoding="utf-8",
    parse_dates=["Order Date"],
    na_values=["", "NA", "N/A", "null"]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is the practical rule:&lt;br&gt;
Use &lt;code&gt;sep&lt;/code&gt; when the delimiter is not a comma. Use &lt;code&gt;encoding&lt;/code&gt; when the file fails to decode. Use &lt;code&gt;na_values&lt;/code&gt; when the source system uses custom labels for missing values. Use &lt;code&gt;parse_dates&lt;/code&gt; when a column should behave like a date.&lt;/p&gt;

&lt;p&gt;You will see &lt;code&gt;usecols&lt;/code&gt;, &lt;code&gt;dtype&lt;/code&gt;, and &lt;code&gt;chunksize&lt;/code&gt; later when you adapt the workflow to larger files.&lt;br&gt;
If you get &lt;code&gt;FileNotFoundError&lt;/code&gt;, Pandas cannot find the file from the current working folder. Run &lt;code&gt;print(Path.cwd())&lt;/code&gt;, move the CSV there, or use a full path.&lt;br&gt;
If the file loads as one giant column, try a different delimiter such as &lt;code&gt;sep=";"&lt;/code&gt; or &lt;code&gt;sep="\t"&lt;/code&gt;.&lt;br&gt;
If Pandas raises &lt;code&gt;UnicodeDecodeError&lt;/code&gt;, try &lt;code&gt;encoding="latin1"&lt;/code&gt; or ask the data owner for the file encoding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect the Data Before You Clean It
&lt;/h2&gt;

&lt;p&gt;Before you clean anything, inspect the dataset. This step protects you from making lazy assumptions.&lt;/p&gt;

&lt;p&gt;Start with the first rows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.head())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then check the last rows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.tail())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;head()&lt;/code&gt; confirms that the file loaded as expected. &lt;code&gt;tail()&lt;/code&gt; helps you catch issues near the bottom of the file. In this dataset, the last row repeats order &lt;code&gt;1008&lt;/code&gt;, which hints at a duplicate.&lt;/p&gt;

&lt;p&gt;Expected result for tail&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9q7y2z9p5wsrr808z3pv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9q7y2z9p5wsrr808z3pv.png" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the size:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.shape)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(16, 8)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The raw dataset has 16 rows and 8 columns.&lt;/p&gt;

&lt;p&gt;Inspect the column names:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.columns)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focvhb7vi6doe6twk470e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focvhb7vi6doe6twk470e.png" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The names make sense to a human, but they are awkward in code because they contain spaces and mixed capitalization.&lt;/p&gt;

&lt;p&gt;Now use &lt;code&gt;info()&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.info()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output will look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2sg1jbu3qb3lxidxmvgc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2sg1jbu3qb3lxidxmvgc.png" width="790" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tells you where the main data quality issues are. You have missing values in &lt;code&gt;Order Date&lt;/code&gt;, &lt;code&gt;Category&lt;/code&gt;, and &lt;code&gt;Unit Price&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;describe()&lt;/code&gt; to inspect numeric columns:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.describe())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Look for values that violate common business rules: negative quantities, zero prices, or extreme outliers. In this small file, the numeric ranges look reasonable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0elaswa0yf5aij9hr2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvz0elaswa0yf5aij9hr2.png" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, check exact data types:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.dtypes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Your text-column dtypes may vary slightly by Pandas version. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym3lquh18y4g6zj2nshm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fym3lquh18y4g6zj2nshm.png" width="490" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Focus on the important question: can Pandas treat dates as dates and numbers as numbers?&lt;br&gt;
If &lt;code&gt;Order Date&lt;/code&gt; appears as &lt;code&gt;object&lt;/code&gt; or &lt;code&gt;str&lt;/code&gt;, Pandas did not parse it as a date. Check that &lt;code&gt;parse_dates=["Order Date"]&lt;/code&gt; matches the column name exactly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean the CSV Data
&lt;/h2&gt;

&lt;p&gt;Now clean the file so you can trust the analysis. Cleaning does not mean forcing the data to look perfect. It means making clear, documented decisions that support your questions. We will start with the column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean Column Names&lt;/strong&gt;&lt;br&gt;
Clean names early because every later step uses these names.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.columns = (
    df.columns
    .str.strip()
    .str.lower()
    .str.replace(" ", "_")
)

print(df.columns)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpvpxuixo7zyn2bvibps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdpvpxuixo7zyn2bvibps.png" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index(['order_id', 'order_date', 'product', 'category', 'quantity',
       'unit_price', 'customer_country', 'payment_method'],
      dtype='object')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now your code can use &lt;code&gt;df["unit_price"]&lt;/code&gt; instead of &lt;code&gt;df["Unit Price"]&lt;/code&gt;.&lt;br&gt;
This may look cosmetic, but it is practical. Clean column names reduce typing errors and make your code easier to scan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Count Missing Values&lt;/strong&gt;&lt;br&gt;
Count missing values after cleaning the column names:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.isna().sum())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdobo2zrbef7gdqlw3bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsdobo2zrbef7gdqlw3bv.png" width="412" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This output gives you three separate cleaning decisions. A missing date, missing category, and missing price do not mean the same thing. Treating them the same way would create weak analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remove Duplicate Rows&lt;/strong&gt;&lt;br&gt;
Check exact duplicates:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.duplicated().sum())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Remove the duplicate row:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = df.drop_duplicates()

print(df.shape)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(15, 8)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You now have 15 rows.&lt;br&gt;
This is safe here because the duplicate row matches exactly. In a real dataset, duplicate IDs with different values require investigation, so do not delete those blindly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle Missing Dates&lt;/strong&gt;&lt;br&gt;
We want monthly revenue, so rows without dates cannot support one of the main goals. Drop rows where &lt;code&gt;order_date&lt;/code&gt; is missing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = df.dropna(subset=["order_date"])

print(df["order_date"].isna().sum())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This choice is specific to our sale file. If your analysis did not use dates, you might keep the row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fill Missing Categories&lt;/strong&gt;&lt;br&gt;
A missing category should not erase a valid sale. Keep the row and label the missing value as unknown:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["category"] = df["category"].fillna("Unknown")

print(df["category"].value_counts(dropna=False))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output should show one &lt;code&gt;Unknown&lt;/code&gt; category.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14mqf8tn9fzah5dq4mnd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14mqf8tn9fzah5dq4mnd.png" width="418" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This lets you include the revenue while still exposing the data-quality issue. That is usually better than hiding the row or pretending you know the category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fill Missing Unit Prices&lt;/strong&gt;&lt;br&gt;
A missing price affects revenue, so you need a reasonable rule. For this dataset, fill the missing price with the median price of the same product:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["unit_price"] = df["unit_price"].fillna(
    df.groupby("product")["unit_price"].transform("median")
)

print(df["unit_price"].isna().sum())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The important method here is &lt;code&gt;transform("median")&lt;/code&gt;. It calculates a median unit price for each product and returns values aligned to the original rows. That alignment lets Pandas fill the missing price with the right product-level median.&lt;/p&gt;

&lt;p&gt;This beats a global median because product prices are not interchangeable. A monitor price and a cable price should not influence a missing notebook price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirm and Fix Data Types&lt;/strong&gt;&lt;br&gt;
Check data types again:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.dtypes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Convert text columns to Pandas string type:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = df.astype({
    "product": "string",
    "category": "string",
    "customer_country": "string",
    "payment_method": "string"
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For this sample file, &lt;code&gt;quantity&lt;/code&gt; and &lt;code&gt;unit_price&lt;/code&gt; should already be numeric. For real files, convert numeric columns explicitly when needed:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["quantity"] = pd.to_numeric(df["quantity"], errors="coerce")
df["unit_price"] = pd.to_numeric(df["unit_price"], errors="coerce")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then check whether conversion introduced missing values:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df[["quantity", "unit_price"]].isna().sum())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use &lt;code&gt;errors="coerce"&lt;/code&gt; when you want invalid numeric text to become missing values. That makes the problem visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a Revenue Column&lt;/strong&gt;&lt;br&gt;
Create the metric you will use for analysis:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["total_sales"] = df["quantity"] * df["unit_price"]

print(df[["order_id", "quantity", "unit_price", "total_sales"]].head())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wgdenfbc18ymwvzsn8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wgdenfbc18ymwvzsn8u.png" width="661" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, the dataset has moved from raw order records to analysis-ready sales data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cleaning Checkpoint&lt;/strong&gt;&lt;br&gt;
Before analysis, run one quick checkpoint:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(df.shape)
print(df.isna().sum())
print(df.dtypes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see 14 rows after removing one duplicate and one row without a date. You should also see no missing values in the fields needed for this project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferwwgq6xubmq3kjp8gby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ferwwgq6xubmq3kjp8gby.png" width="654" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This checkpoint separates cleaning from analysis. You do not want to discover data-quality problems halfway through a revenue summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyze Sales With &lt;code&gt;groupby()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;From now you can start answering business questions. The main tool in this section is &lt;code&gt;groupby()&lt;/code&gt;. In plain English, &lt;code&gt;groupby()&lt;/code&gt; lets you split rows into groups, calculate something for each group, and combine the results into a summary table.&lt;/p&gt;

&lt;p&gt;For example, when you group by category and sum revenue, Pandas does this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Splits rows into category groups.&lt;/li&gt;
&lt;li&gt;Adds &lt;code&gt;total_sales&lt;/code&gt; inside each category.&lt;/li&gt;
&lt;li&gt;Returns one row per category.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Calculate Total Revenue&lt;/strong&gt;&lt;br&gt;
Start with the headline metric:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;total_revenue = df["total_sales"].sum()

print(f"Total revenue: ${total_revenue:,.2f}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total revenue: $1,333.57
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is the total revenue after cleaning. It excludes the duplicate row and the row without an order date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find Revenue by Category&lt;/strong&gt;&lt;br&gt;
Group by category:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category_sales = (
    df.groupby("category", as_index=False)["total_sales"]
    .sum()
    .sort_values("total_sales", ascending=False)
)

print(category_sales)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89n2kkt5zc8qahngsdd1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89n2kkt5zc8qahngsdd1.png" width="499" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;as_index=False&lt;/code&gt; keeps &lt;code&gt;category&lt;/code&gt; as a normal column instead of turning it into the index. For beginner analysis and CSV exports, that format is easier to read and reuse. Furniture leads revenue, even though it has fewer orders than some other categories. High unit prices can matter more than order count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find Top Products&lt;/strong&gt;&lt;br&gt;
Now summarize by product:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;product_sales = (
    df.groupby("product", as_index=False)
    .agg(
        total_sales=("total_sales", "sum"),
        units_sold=("quantity", "sum"),
        orders=("order_id", "count")
    )
    .sort_values("total_sales", ascending=False)
)

print(product_sales)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6dqsvvemofss53lmk2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu6dqsvvemofss53lmk2k.png" width="742" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This table is more useful than a single revenue ranking. It shows revenue, units sold, and order count together. The Monitor generated the most revenue. USB-C Cable sold the most units. Those are different types of performance, and a good analyst keeps them separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find Revenue by Country&lt;/strong&gt;&lt;br&gt;
Group by customer country:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;country_sales = (
    df.groupby("customer_country", as_index=False)["total_sales"]
    .sum()
    .sort_values("total_sales", ascending=False)
)

print(country_sales)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbznbndwjjttmi0g469k4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbznbndwjjttmi0g469k4.png" width="735" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The United States contributes the most revenue in this sample. In a real business, this kind of table can support regional reporting, marketing decisions, or logistics planning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analyze Monthly Revenue&lt;/strong&gt;&lt;br&gt;
Create a month column from &lt;code&gt;order_date&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["order_month"] = df["order_date"].dt.to_period("M").astype("string")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;.dt&lt;/code&gt; accessor lets you work with datetime values. &lt;code&gt;to_period("M")&lt;/code&gt; converts each date to its month, such as &lt;code&gt;2026-03&lt;/code&gt;. That gives you a clean monthly grouping key.&lt;br&gt;
Now summarize revenue by month:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;monthly_sales = (
    df.groupby("order_month", as_index=False)["total_sales"]
    .sum()
    .sort_values("order_month")
)

print(monthly_sales)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0udafi8lolv52but5amp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0udafi8lolv52but5amp.png" width="756" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Revenue rises in March and April because the dataset includes higher-priced furniture orders in those months.&lt;br&gt;
If &lt;code&gt;.dt&lt;/code&gt; raises an error, &lt;code&gt;order_date&lt;/code&gt; is not a datetime column. Convert it with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["order_date"] = pd.to_datetime(df["order_date"], errors="coerce")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then check &lt;code&gt;df["order_date"].isna().sum()&lt;/code&gt; before continuing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter High-Value Orders&lt;/strong&gt;&lt;br&gt;
Use filtering to inspect the rows behind your summary tables.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;high_value_orders = df[df["total_sales"] &amp;gt;= 100].sort_values(
    "total_sales",
    ascending=False
)

print(high_value_orders[["order_id", "product", "customer_country", "total_sales"]])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4q3numhh9l0ag5wt9px6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4q3numhh9l0ag5wt9px6.png" width="800" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only a few orders drive a large share of revenue. This is the kind of detail that summary tables can hide.&lt;br&gt;
For multiple conditions, wrap each condition in parentheses:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;us_high_value_orders = df[
    (df["customer_country"] == "United States") &amp;amp;
    (df["total_sales"] &amp;gt;= 100)
]

print(us_high_value_orders[["order_id", "product", "total_sales"]])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use &lt;code&gt;&amp;amp;&lt;/code&gt; for “and” and &lt;code&gt;|&lt;/code&gt; for “or”. Python’s &lt;code&gt;and&lt;/code&gt; and &lt;code&gt;or&lt;/code&gt; do not work for Pandas Series filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualize the Results
&lt;/h2&gt;

&lt;p&gt;You should not chart everything. Chart the results that become easier to understand visually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Category Revenue Bar Chart&lt;/strong&gt;&lt;br&gt;
Use a bar chart for category comparison:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category_sales.plot(
    kind="bar",
    x="category",
    y="total_sales",
    legend=False,
    title="Revenue by Category"
)

plt.xlabel("Category")
plt.ylabel("Revenue")
plt.tight_layout()
plt.show()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The chart should show Furniture as the highest-revenue category. A stakeholder could understand that pattern faster from the chart than from the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkf615qz1uq2bgnpdgx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkf615qz1uq2bgnpdgx1.png" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monthly Revenue Line Chart&lt;/strong&gt;&lt;br&gt;
Use a line chart for time trends:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;monthly_sales.plot(
    kind="line",
    x="order_month",
    y="total_sales",
    marker="o",
    legend=False,
    title="Monthly Revenue"
)

plt.xlabel("Month")
plt.ylabel("Revenue")
plt.tight_layout()
plt.show()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The line should rise in March and April. In a larger dataset, this chart would help you identify seasonality, campaign effects, or unexpected drops.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frow1c9eks5u9q32fc2tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frow1c9eks5u9q32fc2tb.png" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If charts do not appear, make sure Matplotlib is installed and call &lt;code&gt;plt.show()&lt;/code&gt;. In Jupyter notebooks, &lt;code&gt;%matplotlib inline&lt;/code&gt; can help, but do not use that line in a regular Python script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working With Larger CSV Files
&lt;/h2&gt;

&lt;p&gt;This tutorial uses a small file so you can focus on the workflow. Real CSV files may contain hundreds of thousands or millions of rows. You can still use Pandas, but you should load the data more carefully.&lt;br&gt;
If you only need some columns, use &lt;code&gt;usecols&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;columns_to_load = [
    "Order ID",
    "Order Date",
    "Product",
    "Category",
    "Quantity",
    "Unit Price",
    "Customer Country"
]

large_df = pd.read_csv(
    "sales_data.csv",
    usecols=columns_to_load,
    parse_dates=["Order Date"]
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This reduces memory use and keeps your analysis focused.&lt;br&gt;
If you know the schema, set data types while loading:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;large_df = pd.read_csv(
    "sales_data.csv",
    usecols=columns_to_load,
    parse_dates=["Order Date"],
    dtype={
        "Product": "string",
        "Category": "string",
        "Customer Country": "string",
        "Quantity": "int64",
        "Unit Price": "float64"
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you are unsure about types, load a small sample first:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sample_df = pd.read_csv("sales_data.csv", nrows=1000)

print(sample_df.dtypes)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For very large files, read in chunks:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;revenue_by_country = {}

for chunk in pd.read_csv(
    "sales_data.csv",
    parse_dates=["Order Date"],
    chunksize=5
):
    chunk.columns = (
        chunk.columns
        .str.strip()
        .str.lower()
        .str.replace(" ", "_")
    )

    chunk = chunk.dropna(subset=["order_date", "unit_price"])
    chunk["total_sales"] = chunk["quantity"] * chunk["unit_price"]

    chunk_summary = chunk.groupby("customer_country")["total_sales"].sum()

    for country, revenue in chunk_summary.items():
        revenue_by_country[country] = revenue_by_country.get(country, 0) + revenue

print(revenue_by_country)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6klc5zgws13edyt7nvqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6klc5zgws13edyt7nvqr.png" width="800" height="33"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example uses &lt;code&gt;chunksize=5&lt;/code&gt; only because the sample file is tiny. For real files, try &lt;code&gt;chunksize=50_000&lt;/code&gt; or &lt;code&gt;100_000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pandas also supports &lt;code&gt;dtype_backend="pyarrow"&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df_arrow = pd.read_csv(
    "sales_data.csv",
    dtype_backend="pyarrow"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Treat this as optional. It can help some larger workflows, but it may require PyArrow and does not need to be your first optimization.&lt;br&gt;
Pandas is enough for many CSV analysis tasks. If your data outgrows memory or you need fast SQL over local files, look at DuckDB. If you want a fast DataFrame library, consider Polars. If you need larger-than-memory workflows with familiar Pandas-like patterns, consider Dask. If many people need shared reporting, use a database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export Your Cleaned Data and Summaries
&lt;/h2&gt;

&lt;p&gt;Now finish the project by saving your work.&lt;/p&gt;

&lt;p&gt;Save the cleaned dataset:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.to_csv("cleaned_sales_data.csv", index=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Save the summary tables:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;category_sales.to_csv("category_sales.csv", index=False)
product_sales.to_csv("product_sales.csv", index=False)
country_sales.to_csv("country_sales.csv", index=False)
monthly_sales.to_csv("monthly_sales.csv", index=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Use &lt;code&gt;index=False&lt;/code&gt; because the DataFrame index does not contain business information. If you omit it, Pandas writes the index to the file. When you load that file later, you may see an unwanted &lt;code&gt;Unnamed: 0&lt;/code&gt; column.&lt;/p&gt;

&lt;p&gt;Confirm that your output files exist:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output_files = [
    "cleaned_sales_data.csv",
    "category_sales.csv",
    "product_sales.csv",
    "country_sales.csv",
    "monthly_sales.csv"
]

for file_name in output_files:
    print(file_name, Path(file_name).exists())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Expected output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp1sxeeh59sezic7are1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp1sxeeh59sezic7are1.png" width="748" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now have files you can share, inspect in a spreadsheet, or use in another analysis step.&lt;br&gt;
If Pandas raises a permission error, close the file in Excel or any other program that may have locked it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes When Analyzing CSV Files
&lt;/h2&gt;

&lt;p&gt;Most beginner CSV problems come from file setup, data types, and cleaning decisions.&lt;/p&gt;

&lt;p&gt;If Pandas cannot find your file, check your working folder:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(Path.cwd())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the CSV loads as one giant column, specify the delimiter:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pd.read_csv("sales_data.csv", sep=";")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the file fails with a decoding error, try another encoding:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = pd.read_csv("sales_data.csv", encoding="latin1")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If dates do not work with &lt;code&gt;.dt&lt;/code&gt;, parse them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["order_date"] = pd.to_datetime(df["order_date"], errors="coerce")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If numeric columns load as text, convert them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df["unit_price"] = pd.to_numeric(df["unit_price"], errors="coerce")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you need to update values conditionally, use &lt;code&gt;.loc&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.loc[df["category"].isna(), "category"] = "Unknown"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Avoid this chained-assignment pattern:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df\["category"\][df["category"].isna()] = "Unknown"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If your exported CSV contains an &lt;code&gt;Unnamed: 0&lt;/code&gt; column, export with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df.to_csv("cleaned_sales_data.csv", index=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A good habit solves many of these mistakes, inspect before cleaning, and recheck after cleaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices Checklist
&lt;/h2&gt;

&lt;p&gt;Here is a best practice checklist I have compiled and encourage you to use this checklist when you analyze your own CSV files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the raw file unchanged.&lt;/li&gt;
&lt;li&gt;Inspect the dataset before cleaning it.&lt;/li&gt;
&lt;li&gt;Clean column names early.&lt;/li&gt;
&lt;li&gt;Parse dates before time-based analysis.&lt;/li&gt;
&lt;li&gt;Treat missing values based on column meaning.&lt;/li&gt;
&lt;li&gt;Check duplicates before removing them.&lt;/li&gt;
&lt;li&gt;Convert data types deliberately.&lt;/li&gt;
&lt;li&gt;Create calculated columns for the metrics you need.&lt;/li&gt;
&lt;li&gt;Write analysis around specific questions.&lt;/li&gt;
&lt;li&gt;Sort summary tables before interpreting them.&lt;/li&gt;
&lt;li&gt;Use charts only when they clarify a pattern.&lt;/li&gt;
&lt;li&gt;Save cleaned data separately.&lt;/li&gt;
&lt;li&gt;Export with &lt;code&gt;index=False&lt;/code&gt; unless the index matters.
## Practice Exercise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extend the analysis with three questions. First, find revenue by payment method:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payment_sales = (
    df.groupby("payment_method", as_index=False)["total_sales"]
    .sum()
    .sort_values("total_sales", ascending=False)
)

print(payment_sales)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, find which country bought the most units:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;country_units = (
    df.groupby("customer_country", as_index=False)["quantity"]
    .sum()
    .sort_values("quantity", ascending=False)
)

print(country_units)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, calculate the share of revenue from Furniture:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;furniture_revenue = df.loc[
    df["category"] == "Furniture",
    "total_sales"
].sum()

furniture_share = furniture_revenue / df["total_sales"].sum()

print(f"Furniture revenue share: {furniture_share:.1%}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Export the payment summary:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;payment_sales.to_csv("payment_sales.csv", index=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This exercise follows the same pattern as the main project: ask a question, group or filter the data, calculate the metric, sort the result, and save the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Learned Today
&lt;/h2&gt;

&lt;p&gt;You started with a raw ecommerce CSV and turned it into a practical sales analysis. You created &lt;code&gt;sales_data.csv&lt;/code&gt;, loaded it into Pandas, inspected its structure, cleaned column names, removed a duplicate row, handled missing dates, categories, and prices, and created a &lt;code&gt;total_sales&lt;/code&gt; metric.&lt;br&gt;
Then you used that cleaned data to answer business questions about total revenue, category performance, product performance, country revenue, monthly trends, and high-value orders. You also created two charts and exported cleaned and summarized CSV files.&lt;br&gt;
The final files are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cleaned_sales_data.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;category_sales.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;product_sales.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;country_sales.csv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;monthly_sales.csv&lt;/code&gt;
## Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You analyzed a CSV file with Python and Pandas by following a repeatable workflow: load, inspect, clean, calculate, analyze, visualize, and export. Use the same structure with your own CSV files. Replace the sample file, update the column names, define your questions, and adapt the cleaning rules to your data. The specific dataset will change, but the habit stays the same: understand the file before you trust the numbers.&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>python</category>
      <category>pandas</category>
      <category>analytics</category>
    </item>
    <item>
      <title>How to Integrate YouTube Video Player in a Flutter Application</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Tue, 12 Mar 2024 14:11:20 +0000</pubDate>
      <link>https://dev.to/gunkev/how-to-integrate-youtube-video-player-in-a-flutter-application-4hil</link>
      <guid>https://dev.to/gunkev/how-to-integrate-youtube-video-player-in-a-flutter-application-4hil</guid>
      <description>&lt;p&gt;YouTube is one of the world’s popular social media platforms for hosting and sharing videos. You can find a great list of music, movies, tutorials, documentaries and interviews in any domain there. Many enterprises, corporations and freelancers create and share videos on their various channels to enhance communication and provide a better user experience on the various services they offer. &lt;a href="http://Youtube.com" rel="noreferrer noopener"&gt;YouTube&lt;/a&gt; has become an indispensable social asset in the daily lives of many entrepreneurs and organizations.&lt;/p&gt;

&lt;h2 id="Objectives"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Objectives" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="Objectives"&gt;Objectives&lt;/h2&gt;

&lt;p&gt;This guide will cover the necessary steps you need to follow to integrate YouTube into your Flutter application. In a nutshell, you will build a simple application like in the demo below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/drive/folders/1WNLfxjnQEFAv0rBrqvVVHFQNx9pP4ptF?usp=sharing" rel="noreferrer noopener"&gt;Youtube Flutter Player Demo in Drive&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="Common-Use-Cases-Where-You-Should-Add-YouTube-Integration-in-Your-Mobile-App"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Common-Use-Cases-Where-You-Should-Add-YouTube-Integration-in-Your-Mobile-App" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="Common-Use-Cases-Where-You-Should-Add-YouTube-Integration-in-Your-Mobile-App"&gt;Common Use Cases Where You Should Add YouTube Integration in Your Mobile App&lt;/h2&gt;

&lt;p&gt;Before diving into the main subject, it’s important to know when and where to integrate YouTube into your application. Some of the common use cases are:&lt;/p&gt;

&lt;h3 id="Integrating-Video-Tutorials"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Integrating-Video-Tutorials" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Integrating-Video-Tutorials"&gt;Integrating Video Tutorials&lt;/h3&gt;

&lt;p&gt;One of the easiest ways to enhance user experience and improve engagement is by adding video tutorials from your channel or various subject matters that can interest your audience.&lt;/p&gt;

&lt;p&gt;If you own a course or food application or a simple informational application, integrating YouTube can help users easily and efficiently get access to video content from your various channels that align with their interests.&lt;/p&gt;

&lt;h3 id="Integrating-in-Movies-Application"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Integrating-in-Movies-Application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Integrating-in-Movies-Application"&gt;Integrating in Movies Application&lt;/h3&gt;

&lt;p&gt;Adding movie trials or reviews via YouTube in your movie application allows users to get a preview of a specific movie and helps them decide whether to watch it or not. You can equally add behind-the-scene footage to spice up the app and encourage users to spend longer time on your application hence improving user engagement.&lt;/p&gt;

&lt;h3 id="Informational-oriented-application"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Informational-oriented-application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Informational-oriented-application"&gt;Informational oriented application&lt;/h3&gt;

&lt;p&gt;Informational-centered applications that provide articles, blog posts, and news about a variety of subjects can be enhanced by adding video-related content to improve user comprehension further.&lt;/p&gt;

&lt;h3 id="Monetization"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Monetization" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Monetization"&gt;Monetization&lt;/h3&gt;

&lt;p&gt;Implementing a YouTube player with ad support in your application provides a revenue stream for your application. For example, if you have a food recipe application, you can generate revenue by displaying YouTube videos with ads before, during or after the video.&lt;/p&gt;

&lt;h3 id="Live-Stream-Events"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Live-Stream-Events" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Live-Stream-Events"&gt;Live Stream Events&lt;/h3&gt;

&lt;p&gt;You can embed YouTube live stream video using the YouTube LIVE API into your application easily for real-time interaction with your audience, product launch or Q&amp;amp;A sessions which can improve the kind of services you offer and user engagement.&lt;/p&gt;

&lt;h3 id="Product-Demonstrations"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Product-Demonstrations" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Product-Demonstrations"&gt;Product Demonstrations&lt;/h3&gt;

&lt;p&gt;For educational applications or e-commerce applications, you can integrate YouTube videos to showcase the product features, the benefits of using it and real-life usage to the users in-app.&lt;/p&gt;

&lt;h3 id="Travel"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Travel" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Travel"&gt;Travel&lt;/h3&gt;

&lt;p&gt;A mobile app that includes travel guides or travel-related content with embedded YouTube videos showcasing popular destinations, attractions, or travel tips.&lt;/p&gt;

&lt;p&gt;For example, users can easily visualize popular spots instantly rather than navigating through a list of selected images.&lt;/p&gt;

&lt;h3 id="Fitness-and-Music"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Fitness-and-Music" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Fitness-and-Music"&gt;Fitness and Music&lt;/h3&gt;

&lt;p&gt;In fitness applications, you can incorporate YouTube videos that demonstrate and guide users on how to perform certain movements.&lt;/p&gt;

&lt;p&gt;Keep in mind that while integrating these YouTube features into your mobile application, it’s important to always ensure compliance with YouTube’s terms of service.&lt;/p&gt;

&lt;h2 id="Integrating-YouTube-in-a-Flutter-Application"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Integrating-YouTube-in-a-Flutter-Application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="Integrating-YouTube-in-a-Flutter-Application"&gt;Integrating YouTube in a Flutter Application&lt;/h2&gt;

&lt;p&gt;In this section, we will work you through how to integrate a Youtube player into your &lt;a href="https://www.learn-dev-tools.blog/what-is-the-flutter-framework-a-beginners-introduction-to-flutter/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; application.&lt;/p&gt;

&lt;h3 id="Prerequisites"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Prerequisites" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Prerequisites"&gt;Prerequisites&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/youtube_player_flutter" rel="noreferrer noopener"&gt;YouTube Player Flutter&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;Basic Dart Knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id="Creating-a-Flutter-Application"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Creating-a-Flutter-Application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Creating-a-Flutter-Application"&gt;Creating a Flutter Application&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.learn-dev-tools.blog/how-to-install-flutter-on-windows-in-a-few-steps/" rel="noopener noreferrer"&gt;Install Flutter&lt;/a&gt;, open your Android Studio editor and create a new Flutter project or open your terminal and run the following command to get started with a Flutter project.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;flutter create example_project_youtube&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;example_project_youtube&lt;/code&gt; is the name of the project, and you can replace it with any name you want. Once the project is complete, choose an emulator or connect your phone and run the following command to launch it.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;flutter run&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="Adding-YouTube-Package"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Adding-YouTube-Package" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Adding-YouTube-Package"&gt;Adding YouTube Package&lt;/h3&gt;

&lt;p&gt;To add the &lt;code&gt;youTube_player_flutter&lt;/code&gt; package, open another terminal and run the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;flutter pub add youtube_player_flutter&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;youtube_player_flutter&lt;/code&gt; plugin for playing or streaming inline YouTube videos using the official iFrame player API. This plugin supports both Android and iOS.&lt;/p&gt;

&lt;p&gt;This will add the &lt;code&gt;youtube_player_flutter&lt;/code&gt; package in the dependencies section of your &lt;code&gt;pubspec.yaml&lt;/code&gt; located at the root of your project.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  youtube_player_flutter: ^8.1.2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now create two dart files in which one will contain the video JSON data model and the last will be the YouTube player page.&lt;/p&gt;

&lt;p&gt;We also have the &lt;code&gt;main.dart&lt;/code&gt; file which looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_app_youtube/screens/YoutubeScreen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        useMaterial3: true,
      ),
      home: const YouTubeScreen(),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a &lt;code&gt;lib/model/VideoModel.dart&lt;/code&gt; and add the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import 'dart:convert';

class VideoModel {
  int? id;
  String? name;
  String? url;

  VideoModel(this.id, this.name, this.url);

  VideoModel.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    id = json["id"];
    name = json['name'].toString();
    url = json['url'];
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create YouTubeVideoScreen.dart&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

class YouTubeScreen extends StatefulWidget {

  const YouTubeScreen({Key? key}) : super(key: key);

  @override
  _YouTubeScreenState createState() =&amp;gt; _YouTubeScreenState();
}

class _YouTubeScreenState extends State&amp;lt;YouTubeScreen&amp;gt; {
  String? videoId;

  @override
  void initState() {
    super.initState();
    videoId = 't31fj-0t3Dw';
  }

  @override
  Widget build(BuildContext context) {
    return YoutubePlayer(
      controller: YoutubePlayerController(
        initialVideoId: videoId!,
        flags: const YoutubePlayerFlags(
          autoPlay: true, 
          mute: false
        )
      )
    );
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A Youtube video url is generally in the form:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://www.youtube.com/watch?v='Your video id here'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;v&lt;/code&gt; in the url contains the &lt;code&gt;id&lt;/code&gt; of your video. So put this id in the &lt;code&gt;videoId&lt;/code&gt; variable and test your application&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhackmd.io%2F_uploads%2FHkUCW6w56.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhackmd.io%2F_uploads%2FHkUCW6w56.jpg" alt="integrate_youtube_vide" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="Incorporating-Features-and-Customizing-YouTube-Player"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Incorporating-Features-and-Customizing-YouTube-Player" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Incorporating-Features-and-Customizing-YouTube-Player"&gt;Incorporating Features and Customizing YouTube Player&lt;/h3&gt;

&lt;h3 id="Show-Video-Progression-Indicator"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Show-Video-Progression-Indicator" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Show-Video-Progression-Indicator"&gt;Show Video Progression Indicator&lt;/h3&gt;

&lt;p&gt;By default, the &lt;code&gt;showVideoProgressIndicator&lt;/code&gt; is set to true, you can set it to false if you don’t want to see the video progression indicator.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;showVideoProgressIndicator: false&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="Progress-Indicator-Color"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Progress-Indicator-Color" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Progress-Indicator-Color"&gt;Progress Indicator Color&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;progressIndicatorColor&lt;/code&gt; attribute to change the color of the video progress indicator. For example, below we have changed the color to green:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;progressIndicatorColor: Colors.green&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="Setting-FullScreen-Mode"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Setting-FullScreen-Mode" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Setting-FullScreen-Mode"&gt;Setting FullScreen Mode&lt;/h3&gt;

&lt;p&gt;To support Full screen you need to wrap the player in the &lt;code&gt;YoutubePlayerBuilder&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;YoutubePlayerBuilder(
  player: YoutubePlayer(
    controller: _controller,
  ),
  builder: (context, player){
    return Column(
      children: [
        player,
      ]
    );
  }
),&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="Adding-Players-Control"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Adding-Players-Control" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Adding-Players-Control"&gt;Adding Players Control&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;YoutubePlayerFlags&lt;/code&gt; allows you to define player flags. In the example below, we set the &lt;code&gt;autoplay&lt;/code&gt; to false, disabled the audio by setting &lt;code&gt;mute&lt;/code&gt; to true, enabled caption, and hide controls, looped the video and disabled video HD mode.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;YoutubePlayerFlags(
  autoPlay: false,
  mute: false,
  enableCaption: true,
  loop: true,
  forceHD: false,
  hideControls: true
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you want to know more you can check the &lt;a href="https://pub.dev/documentation/youtube_player_flutter/latest/youtube_player_flutter/YoutubePlayerFlags-class.html" rel="noreferrer noopener"&gt;YoutublePlayerFlags Class&lt;/a&gt; and equally learn how to &lt;a href="https://pub.dev/packages/youtube_player_flutter#want-to-customize-the-player" rel="noreferrer noopener"&gt;customize other features&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="What’s-Next"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#What%E2%80%99s-Next" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="What’s-Next"&gt;What’s Next&lt;/h2&gt;

&lt;h3 id="Conclusion"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Conclusion" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Conclusion"&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;In this guide, we have learned how to integrate a YouTube video player into a Flutter application. By following the step-by-step guide and code examples provided, you can enhance your app's functionality and user experience by seamlessly incorporating YouTube videos. Remember to handle error cases gracefully and consider implementing additional features like Playback Speed and playlist integration to further enrich your app.&lt;/p&gt;

&lt;p&gt;Flutter YouTube Player increases the productivity of your applicant by providing visual and dynamic content that makes your application both informative and engaging. You can add a YouTube video player in any application as you can see through the use cases provided earlier.&lt;/p&gt;

&lt;p&gt;With the power of Flutter and YouTube integration, the possibilities are endless.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;br&gt;&lt;a href="https://github.com/Gunkev/youtube_player_flutter" rel="noreferrer noopener"&gt;Full GitHub Code of Tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="Resources"&gt;&lt;a href="https://hackmd.io/OVg4JekzQImhG3ym6BP07A#Resources" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="Resources"&gt;Resources&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/youtube_player_flutter" rel="noopener noreferrer"&gt;https://pub.dev/packages/youtube_player_flutter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/why-considering-video-integration-mobile-apps-profitable-abbas/" rel="noopener noreferrer"&gt;https://www.linkedin.com/pulse/why-considering-video-integration-mobile-apps-profitable-abbas/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>mobile</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Opensea vs Blur: Which Platform Suits Your Needs?</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Tue, 12 Mar 2024 11:23:06 +0000</pubDate>
      <link>https://dev.to/gunkev/opensea-vs-blur-which-platform-suits-your-needs-1igj</link>
      <guid>https://dev.to/gunkev/opensea-vs-blur-which-platform-suits-your-needs-1igj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AkRb34JeuGEOgQueNX921iA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AkRb34JeuGEOgQueNX921iA.jpeg" alt="" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the ever-evolving world of digital assets and non-fungible tokens (NFTs), choosing the right platform for buying, selling, or exploring NFTs can be a daunting task. Two of the leading platforms in this space are OpenSea and Blur. Each has its unique features, community, and approach to the NFT market. In this post, we’ll dive deep into the differences between OpenSea and Blur, helping you decide which platform best suits your needs as an NFT enthusiast, artist, or investor.&lt;/p&gt;

&lt;h3&gt;OpenSea: The Gateway to&amp;nbsp;NFTs&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AsicmcqKUFNxiEpI6-H5iJQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2AsicmcqKUFNxiEpI6-H5iJQ.png" alt="" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opensea.io/" rel="noreferrer noopener"&gt;OpenSea&lt;/a&gt; has established itself as the go-to marketplace for &lt;a href="https://docs.alchemy.com/docs/nft-transactions" rel="noreferrer noopener"&gt;NFT transactions&lt;/a&gt;, often referred to as the &lt;a href="https://www.ebay.com/" rel="noreferrer noopener"&gt;eBay&lt;/a&gt; for digital assets. It’s designed to cater to a broad audience, from beginners in the NFT space to seasoned collectors and investors.&lt;/p&gt;

&lt;h3&gt;Key Features:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diverse Marketplace&lt;/strong&gt;: OpenSea boasts an expansive collection of NFTs across various categories, including art, domain names, virtual worlds, trading cards, and collectibles. This diversity makes it an attractive platform for users with varied interests.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;User-Friendly Interface&lt;/strong&gt;: The platform is known for its intuitive and easy-to-navigate interface, making it accessible for users of all levels of experience. Whether you’re creating, buying, or selling NFTs, the process is straightforward.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Cross-Blockchain Support&lt;/strong&gt;: OpenSea supports multiple blockchains, including Ethereum, Polygon, and Klaytn. This cross-chain functionality allows users to explore a wider range of NFTs and engage with different blockchain communities.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Community and Resources&lt;/strong&gt;: OpenSea offers extensive resources for new users, including guides, FAQs, and a supportive community. These resources are invaluable for those new to the NFT space.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Wide range of NFTs&lt;/li&gt;



&lt;li&gt;User-friendly for beginners&lt;/li&gt;



&lt;li&gt;Strong community support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Transaction fees can be high on certain blockchains&lt;/li&gt;



&lt;li&gt;Some users have reported issues with customer service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Blur: The Professional’s NFT Marketplace&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ABOngPgujtz0nA9eOWyxj8g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F1%2ABOngPgujtz0nA9eOWyxj8g.png" alt="" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blur.io/" rel="noreferrer noopener"&gt;Blur&lt;/a&gt; positions itself as a platform for professional NFT traders and artists. It’s known for its advanced features and analytics, catering to users looking for a more data-driven approach to the NFT market.&lt;/p&gt;

&lt;h3&gt;Key Features:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Trading Tools&lt;/strong&gt;: Blur offers sophisticated tools and analytics for users to track the market, analyze trends, and make informed decisions. This is particularly beneficial for professional traders and investors.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Batch Buying and Selling&lt;/strong&gt;: The platform allows users to buy or sell multiple NFTs in a single transaction, streamlining the process and saving on transaction fees.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Rewards System&lt;/strong&gt;: Blur incentivizes platform activity through a rewards system, offering BLUR tokens for actions like listing NFTs and participating in auctions. This can be particularly attractive for active traders and sellers.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Focus on Speed and Efficiency&lt;/strong&gt;: The platform is optimized for speed, aiming to provide a seamless and efficient experience for high-frequency traders.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Pros:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Advanced tools and analytics&lt;/li&gt;



&lt;li&gt;Incentivized participation&lt;/li&gt;



&lt;li&gt;Efficient batch transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Cons:&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Might be overwhelming for beginners&lt;/li&gt;



&lt;li&gt;More focused on trading than the community aspect&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Comparing OpenSea and&amp;nbsp;Blur&lt;/h3&gt;

&lt;p&gt;When deciding between OpenSea and Blur, consider your objectives and preferences in the NFT space.&lt;/p&gt;

&lt;h3&gt;For Beginners and Enthusiasts:&lt;/h3&gt;

&lt;p&gt;If you’re new to NFTs or looking for a broad marketplace with diverse offerings, OpenSea is likely the better choice. Its user-friendly interface, extensive resources, and supportive community make it an ideal starting point for exploring the world of NFTs.&lt;/p&gt;

&lt;h3&gt;For Artists:&lt;/h3&gt;

&lt;p&gt;Artists looking to showcase their work might find OpenSea more accommodating due to its larger audience and the diversity of its user base. However, Blur’s advanced tools and analytics could also appeal to artists interested in market trends and the trading aspect of their work.&lt;/p&gt;

&lt;h3&gt;For Professional Traders and Investors:&lt;/h3&gt;

&lt;p&gt;For those with experience in the NFT market, particularly traders and investors seeking advanced tools and analytics, Blur stands out. Its focus on speed, efficiency, and the rewards system makes it a compelling option for users looking to maximize their engagement and returns.&lt;/p&gt;

&lt;h3&gt;Community vs. Trading&amp;nbsp;Focus:&lt;/h3&gt;

&lt;p&gt;OpenSea fosters a broader community of NFT enthusiasts, making it a more suitable platform for users interested in the social and discovery aspects of NFTs. On the other hand, Blur appeals to users with a strong focus on the trading and investment side, offering tools and features that cater to this demographic.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Choosing between OpenSea and Blur ultimately depends on your specific needs, level of expertise, and interests within the NFT space. OpenSea offers a welcoming environment for newcomers and a diverse marketplace for collectors and artists, emphasizing community and accessibility. Blur, meanwhile, caters to the more professional segment of the market, providing advanced tools and analytics for serious traders and investors.&lt;/p&gt;

&lt;p&gt;Before making your decision, consider what you value most in an NFT platform — whether it’s the community, diversity of assets, trading&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Build an Electronic Commerce Store with Medusajs</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Wed, 25 Oct 2023 12:55:57 +0000</pubDate>
      <link>https://dev.to/gunkev/how-to-build-an-electronic-commerce-store-with-medusajs-4p5i</link>
      <guid>https://dev.to/gunkev/how-to-build-an-electronic-commerce-store-with-medusajs-4p5i</guid>
      <description>&lt;p&gt;The demand and supply of goods and services keep evolving daily. Businesses need to keep an eye on these evolutions to adopt the right ecommerce platform to distribute their goods and services to various end users. &lt;/p&gt;

&lt;p&gt;Headless commerce is becoming increasingly popular because of its adaptability and scalability. A great option for constructing a headless commerce store is Medusa, an open source and scalable ecommerce engine. Next.js offers a robust framework for building storefronts. &lt;/p&gt;

&lt;p&gt;With the help of Medusa and Next.js, you can develop a powerful and scalable online store that is tailored to the particular requirements of your company or business. Regardless of your experience as a developer or online retailer, this tutorial will guide you on building and customizing an electronic store. &lt;/p&gt;

&lt;p&gt;Here’s a demo of the application with Medusa and Next.js by following this tutorial:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prod-files-secure.s3.us-west-2.amazonaws.com/e27708d9-808e-4b35-b986-9f26951398de/5060b0c4-c5a5-4c04-a058-74cf16653819/demo_medusa_electronic_store.mp4" rel="noopener noreferrer"&gt;demo medusa electronic store.mp4&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Will You Learn?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set up a Storefront with Medusa’s &lt;strong&gt;&lt;a href="https://docs.medusajs.com/starters/nextjs-medusa-starter" rel="noopener noreferrer"&gt;Next.js default starter template&lt;/a&gt;.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add product using the admin dashboard.&lt;/li&gt;
&lt;li&gt;Display products in your storefront.&lt;/li&gt;
&lt;li&gt;Customize your storefront.&lt;/li&gt;
&lt;li&gt;Add a Search option to your store.&lt;/li&gt;
&lt;li&gt;Add a Payment option to your store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the code for this tutorial in this &lt;a href="https://github.com/Gunkev/electronic-storefront-medusa" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Next.js?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; is an open-source React framework for building user-friendly web applications. &lt;a href="https://beta.reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; is a JavaScript library for building interactive user interfaces. Next.js enables server-side rendering and requires minimal or no configuration.&lt;/p&gt;

&lt;p&gt;Medusa offers two storefront starters: Next.js and Gatsby. It's easy to install them with the CLI command. Medusa Next.js storefront has many ecommerce pre-build features like a shopping cart, product display, payment processing, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Medusa?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is an &lt;a href="https://medusajs.com/blog/composable-ecommerce-platform/" rel="noopener noreferrer"&gt;open-source digital commerce infrastructure&lt;/a&gt; built with Node.js and provides many ecommerce features such as RMA flows, product and collection management, order management, customization, and more. &lt;/p&gt;

&lt;p&gt;These are some features that set Medusa apart from other ecommerce platforms. Medusa also offers a great developer experience, allowing developers to create amazing and scalable stores with minimal effort.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medusajs.com/blog/composable-commerce/" rel="noopener noreferrer"&gt;Medusa's headless architecture&lt;/a&gt; enables you to build your store with the framework or language of your choice. There are no constraints, as you can use &lt;a href="https://docs.medusajs.com/api/admin" rel="noopener noreferrer"&gt;Medusa's API&lt;/a&gt; to connect the &lt;a href="https://docs.medusajs.com/admin/quickstart" rel="noopener noreferrer"&gt;Medusa admin&lt;/a&gt; to the storefront to perform actions like the product, customer, order, payment management, and many more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating your Electronic Commerce Platform
&lt;/h2&gt;

&lt;p&gt;This section highlights the steps to build your electronic store with Next.js and Medusa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before you begin, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Nodejs&lt;/a&gt; version 14 or later&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://yarnpkg.com/" rel="noopener noreferrer"&gt;Yarn&lt;/a&gt; or Npm(This tutorial uses Yarn)&lt;/li&gt;
&lt;li&gt;A code editor like &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://www.algolia.com/" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;A &lt;a href="http://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;S3 for file upload service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will find all the resources used in this tutorial, such as &lt;a href="https://github.com/Gunkev/electronic-storefront-medusa/tree/main/public/store%20images" rel="noopener noreferrer"&gt;icons and images in the Git repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Set Up the Medusa Server for the Electronic Store
&lt;/h3&gt;

&lt;p&gt;If you have everything installed, follow these steps to set up your &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Medusa CLI tool with the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn global add @medusajs/medusa-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create your Medusa application with this command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create medusa-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon executing, this command will ask you where you which to install your project. Enter a name there; in this tutorial, it is &lt;code&gt;ElectronicStore&lt;/code&gt;. Then, press enter. For this tutorial, choose the &lt;code&gt;Medusa-starter-default&lt;/code&gt; for the backend and the &lt;code&gt;Next.js starter&lt;/code&gt; template for the storefront. &lt;/p&gt;

&lt;p&gt;Normally you should have the same information as the screenshot below.&lt;/p&gt;

&lt;p&gt;![How to Build an Electronic Commerce Store with Medusa and Nextjs]&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/starter-1024x302.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/starter-1024x302.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon the execution of this command, Medusa will create three folders in the &lt;code&gt;ElectronicStore&lt;/code&gt;: storefront, admin, and backend.&lt;/p&gt;

&lt;p&gt;To start each part of the application, open the three command line/terminal tabs and run the following command with respect to each folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Medusa server&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ElectronStore/backend
yarn start

&lt;span class="c"&gt;# Admin&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ElectronStore/admin
yarn start

&lt;span class="c"&gt;# Storefront&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ElectronStore/storefront
yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After executing those commands, visit &lt;code&gt;localhost:8000&lt;/code&gt; to view your storefront.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F39ee85f8-0c84-42c8-8d2f-e600fd4dfb6a%2Fstoref.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F39ee85f8-0c84-42c8-8d2f-e600fd4dfb6a%2Fstoref.png" alt="storef.PNG" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, visit your admin dashboard by opening your browser on &lt;code&gt;localhost:7000&lt;/code&gt;. 7000 is the default admin port.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/admin-1024x552.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/admin-1024x552.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step consists of styling your storefront. This tutorial uses &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; very useful for building modern websites. Note that you can equally set up your &lt;a href="https://docs.medusajs.com/admin/quickstart" rel="noopener noreferrer"&gt;Medusa admin&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/starters/nextjs-medusa-starter" rel="noopener noreferrer"&gt;Medusa storefront&lt;/a&gt;, and &lt;a href="https://docs.medusajs.com/development/backend/install" rel="noopener noreferrer"&gt;Medusa Backend&lt;/a&gt; separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Styling the Electronic Commerce Storefront
&lt;/h2&gt;

&lt;p&gt;This section shows how to style your storefront using &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt;. With &lt;code&gt;create-medusa-app&lt;/code&gt;, the Next.js starter template already contains Tailwind. However, you need to configure the &lt;code&gt;tailwind.config.js&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./app/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pages/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Or if using `src` directory:&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.{js,ts,jsx,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have successfully added Tailwind to your project, you must customize your storefront.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customizing the Storefront
&lt;/h3&gt;

&lt;p&gt;Start by changing the style of the header &lt;code&gt;storefront/src/module/layout/templates/nav/index.tsx&lt;/code&gt; with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Nav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isHome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsHome&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pathname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;setIsHome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;setIsHome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toggle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMobileMenu&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sticky top-0 inset-x-0 z-50 group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;!fixed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isHome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative h-16 px-8 mx-auto transition-colors bg-white border-b border-transparent duration-200 group-hover:bg-white group-hover:border-gray-200 border-b-[#f1f1f1]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-gray-900 flex items-center justify-between w-full h-full text-small-regular transition-colors duration-200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 basis-0 h-full flex items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"block small:hidden"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hamburger&lt;/span&gt; &lt;span class="na"&gt;setOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden small:block h-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropdownMenu&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center h-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl-semi uppercase"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/logo.png"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-x-6 h-full flex-1 basis-0 justify-end"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden small:flex items-center gap-x-6 h-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FEATURE_SEARCH_ENABLED&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DesktopSearchModal&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/account"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Account&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CartDropdown&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MobileMenu&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Nav&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code above changes the navbar by adding a border and logo.&lt;/p&gt;

&lt;p&gt;To change the banner image, locate your &lt;code&gt;storefront/src/modules/home/components/hero/index.tsx&lt;/code&gt;, which is the hero section and replace it with the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-[90vh] w-full relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-white absolute inset-0 z-10 flex flex-col justify-center items-center text-center small:text-left small:justify-end small:items-start small:p-32"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl-semi mb-4 drop-shadow-md shadow-black"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Hi 👋 welcome to my electronic shop
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base-regular max-w-[32rem] mb-6 drop-shadow-md shadow-black"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          The best electronic devices to help you in your daily tasks both in
          the professional and personal life.
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/store"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Explore products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-white h-[90vh] w-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        The best electronic devices to help you in your daily tasks both
        in the professional and personal life.
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/hero2.jpg"&lt;/span&gt;
        &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fill"&lt;/span&gt;
        &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"eager"&lt;/span&gt;
        &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;objectFit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cover"&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"computer"&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"absolute inset-0"&lt;/span&gt;
        &lt;span class="na"&gt;draggable&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-black h-[90vh] w-full absolute top-0 opacity-50"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Hero&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code block adds a banner image and text to the hero section. &lt;/p&gt;

&lt;p&gt;Next, change how you display feature products in the home section. &lt;code&gt;storefront/src/module/home/components/featured-products/index.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FeaturedProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex flex-col items-center text-center mb-16&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base-regular text-gray-600 mb-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Latest products
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl-regular text-gray-900 max-w-lg mb-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Our newest electronic gadgets to make your life smooth.
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/store"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Explore products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       ...
  )
}

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above changes the text of feature products of your store.&lt;/p&gt;

&lt;p&gt;Change the section just before the footer  &lt;code&gt;storefront/src/modules/layout/components/footer-cta/index.tsx&lt;/code&gt; by adding the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FooterCTA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-100 w-full"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"content-container flex flex-col-reverse gap-y-8 small:flex-row small:items-center justify-between py-16 relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl-semi"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Check out the lastest gadgets&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mt-6"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/store"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Explore products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UnderlineLink&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative w-full aspect-square small:w-[35%] small:aspect-[28/36]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
            &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/hero3.jpg"&lt;/span&gt;
            &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
            &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fill"&lt;/span&gt;
            &lt;span class="na"&gt;objectFit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"cover"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"absolute inset-0"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;FooterCTA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you are changing the default text as well as the default image. &lt;/p&gt;

&lt;p&gt;Now move to the footer file &lt;code&gt;storefront/src/modules/layout/components/footer-nav/index.tsx&lt;/code&gt; and replace it with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FooterNav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;collections&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCollections&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"content-container flex flex-col gap-y-8 pt-16 pb-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col gap-y-6 xsmall:flex-row items-start justify-between"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl-semi uppercase"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/logo.png"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"py-5 text-sm text-[#999]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Get the best electronics from the best place
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col-reverse gap-y-0 justify-center xsmall:items-center xsmall:flex-row xsmall:justify-between"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xsmall-regular text-gray-500"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              © Copyright 2023 ES store
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"min-w-[316px] flex xsmall:justify-end"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CountrySelect&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-small-regular grid grid-cols-3 gap-x-16"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col gap-y-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-base-semi"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Collections&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;clsx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid grid-cols-1 gap-y-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid-cols-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;collections&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/collections/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;FooterNav&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet above adds new links and a logo in the footer. In the last part of the footer, change the background colour &lt;code&gt;storefront/src/modules/layout/components/medusa-cta/index.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MedusaCTA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"py-4 flex justify-center items-center w-full bg-[#333]"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"content-container flex justify-center flex-1"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://www.medusajs.com"&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PoweredBy&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head to &lt;code&gt;storefront/src/pages/index.tsx&lt;/code&gt; to modify the meta information of the home page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextPageWithLayout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Head&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Get the best electroics for your home and office from the best stores online with secure payment methods."&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Hero&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FeaturedProducts&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the end, you should have a store similar to the screenshot below: &lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store1-1024x470.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store1-1024x470.png&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Products in the Electronic Commerce Store
&lt;/h2&gt;

&lt;p&gt;By default, Medusa comes with some products on the storefront. You need to add your products. Medusa commonly uses a &lt;a href="https://docs.medusajs.com/development/file-service/overview" rel="noopener noreferrer"&gt;file service&lt;/a&gt; plugin to add products to the admin dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing and Integrating a File Service Plugin (S3)
&lt;/h3&gt;

&lt;p&gt;Simple Storage Service (S3) is a powerful open-source object storage suite available on any public and private cloud. Medusa uses file service plugins to host files such as images.&lt;/p&gt;

&lt;p&gt;This tutorial will use &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html" rel="noopener noreferrer"&gt;AWS S3&lt;/a&gt;, but you can use other file services like &lt;a href="https://docs.medusajs.com/plugins/file-service/spaces" rel="noopener noreferrer"&gt;Space&lt;/a&gt; and &lt;a href="https://docs.medusajs.com/plugins/file-service/minio" rel="noopener noreferrer"&gt;Minio&lt;/a&gt;. You need to have an &lt;a href="https://console.aws.amazon.com/console/home?nc2=h_ct&amp;amp;src=header-signin" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; and &lt;a href="https://docs.medusajs.com/plugins/file-service/s3" rel="noopener noreferrer"&gt;create a bucket&lt;/a&gt; if you have none. Equally, you need to &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys" rel="noopener noreferrer"&gt;create an access and secret key&lt;/a&gt; that you will use in the next section to build your store.&lt;/p&gt;

&lt;p&gt;When you are done with that, install the S3 plugin in the backend with the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;medusa&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, open your &lt;code&gt;backend/.env&lt;/code&gt; file and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3_URL=&amp;lt;YOUR_BUCKET_URL&amp;gt;
S3_BUCKET=&amp;lt;YOUR_BUCKET_NAME&amp;gt;
S3_REGION=&amp;lt;YOUR_BUCKET_REGION&amp;gt;
S3_ACCESS_KEY_ID=&amp;lt;YOUR_ACCESS_KEY_ID&amp;gt;
S3_SECRET_ACCESS_KEY=&amp;lt;YOUR_SECRET_ACCESS_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_BUCKET_URL&amp;gt;&lt;/code&gt; corresponds to the URL of your bucket it's generally in the form: &lt;code&gt;http://&amp;lt;YOUR_BUCKET_NAME&amp;gt;.s3-website-&amp;lt;YOUR_BUCKET_REGION&amp;gt;.amazonaws.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_BUCKET_NAME&amp;gt;&lt;/code&gt;: The name of your S3 bucket&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_ACCESS_KEY_ID&lt;/code&gt;: The access key which was created earlier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_SECRET_ACCESS_KEY&amp;gt;&lt;/code&gt;: The secret key created earlier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To complete the S3 integration, add the following configuration to the array of plugins in the &lt;code&gt;backend/medusa-config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`medusa-file-s3`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;s3_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_REGION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;access_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;secret_access_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, edit &lt;code&gt;storefront/next.config.js&lt;/code&gt; file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;YOUR_BUCKET_NAME&amp;gt;.s3.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the domain from where you are uploading your images.  corresponds to the name of your bucket. Once you have finished setting up S3, you can add electronic products to your store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Products with Medusa Admin
&lt;/h3&gt;

&lt;p&gt;To add products to your store, head to the admin dashboard (&lt;code&gt;localhost:7000&lt;/code&gt;). The default login credentials are &lt;code&gt;admin@medusa-test.com&lt;/code&gt; and &lt;code&gt;supersecret&lt;/code&gt;. The Medusa admin dashboard connects the Medusa server to help you to manage your store.&lt;/p&gt;

&lt;p&gt;Once you logged in, you should have a similar interface as below. Here you can manage your &lt;a href="https://docs.medusajs.com/user-guide/products/" rel="noopener noreferrer"&gt;products&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/user-guide/orders/" rel="noopener noreferrer"&gt;orders&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/user-guide/customers/manage" rel="noopener noreferrer"&gt;customer&lt;/a&gt; settings, and more.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/dash-1024x464.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/dash-1024x464.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Head to the product page and delete all the default products by clicking on the three dots at the end and selecting &lt;code&gt;delete&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/deleteprod-1024x176.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/deleteprod-1024x176.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing all the products, click on &lt;code&gt;add new product&lt;/code&gt; button to add some electronic products to the ecommerce store.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store3-1024x464.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store3-1024x464.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you go to &lt;code&gt;localhost:8000/store&lt;/code&gt; you will see your newly-added products.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store2-1024x469.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store2-1024x469.png&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration
&lt;/h2&gt;

&lt;p&gt;Now is the time to integrate the two main services: Search and Payment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Algolia Integration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.algolia.com/" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; is an open-source UI search JavaScript library. It allows you to add advanced search functionalities to your store. Adding this search functionality to your store will greatly improve the user experience and &lt;a href="https://medusajs.com/blog/importance-of-optimizing-internal-search-in-ecommerce-websites/" rel="noopener noreferrer"&gt;optimize internal search&lt;/a&gt; in the electronic commerce store.&lt;/p&gt;

&lt;p&gt;To add Algolia to your application, you need to possess an &lt;a href="https://www.algolia.com/users/sign_up" rel="noopener noreferrer"&gt;Algolia account&lt;/a&gt;. Upon creating your account, follow &lt;a href="https://docs.medusajs.com/plugins/search/algolia#retrieve-api-keys" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt; to create an &lt;a href="https://docs.medusajs.com/plugins/search/algolia#create-an-algolia-app" rel="noopener noreferrer"&gt;Algolia application&lt;/a&gt; and then, get your Algolia Application ID, Admin API Key, and Search-Only API Key.&lt;/p&gt;

&lt;p&gt;Next, install the Algolia plugin in your store with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add medusa-plugin-algolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add this environment variable to your Medusa backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALGOLIA_APP_ID=&amp;lt;YOUR_APP_ID&amp;gt;
ALGOLIA_ADMIN_API_KEY=&amp;lt;YOUR_ADMIN_API_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;YOUR_APP_ID&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;YOUR_ADMIN_API_KEY&amp;gt;&lt;/code&gt; correspond to the Application ID and Admin API Key mentioned earlier. You can find them on the &lt;a href="https://www.algolia.com/account/api-keys/" rel="noopener noreferrer"&gt;API Keys page&lt;/a&gt; under your Algolia account.&lt;/p&gt;

&lt;p&gt;Add the following item into the &lt;code&gt;plugins&lt;/code&gt; array in &lt;code&gt;medusa-config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`medusa-plugin-algolia`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;application_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALGOLIA_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;admin_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALGOLIA_ADMIN_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;searchableAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;attributesToRetrieve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;handle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thumbnail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;variants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;variant_sku&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;options&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;collection_title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;collection_handle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;images&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;searchableAttributes&lt;/code&gt; are attributes in a product that are searchable while &lt;code&gt;attributesToRetrieve&lt;/code&gt; are attributes to retrieve for each product result.&lt;/p&gt;

&lt;p&gt;Now, add the search UI to the storefront so that users can effectively perform product research. Fortunately, Next.js storefront Algolia integration is available out-of-the-box. To get everything ready, make sure the search feature is set to &lt;code&gt;true&lt;/code&gt; in the &lt;code&gt;store.config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the environmental variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_SEARCH_APP_ID=&amp;lt;YOUR_APP_ID&amp;gt;
NEXT_PUBLIC_SEARCH_API_KEY=&amp;lt;YOUR_SEARCH_API_KEY&amp;gt;
NEXT_PUBLIC_SEARCH_INDEX_NAME=products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_APP_ID&amp;gt;&lt;/code&gt; corresponds to the Application ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;YOUR_SEARCH_API_KEY&amp;gt;&lt;/code&gt; corresponds to the Search-Only API Key. You can find this information in your Algolia &lt;a href="https://docs.medusajs.com/plugins/search/algolia#retrieve-api-keys" rel="noopener noreferrer"&gt;API Keys section&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To complete this integration, change the code in &lt;code&gt;src/lib/search-client.ts&lt;/code&gt; to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;algoliasearch&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;algoliasearch/lite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SEARCH_APP_ID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_SEARCH_API_KEY&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;algoliasearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SEARCH_INDEX_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_INDEX_NAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run your storefront and backend, you will notice that the search feature is available.&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/search-1024x553.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/search-1024x553.png&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Payment Integration with&lt;/strong&gt; Stripe
&lt;/h3&gt;

&lt;p&gt;This section will guide you through how to set up Stripe payment methods in your Medusa backend, admin, and storefront. &lt;a href="https://stripe.com/" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; is one of the simplest ways to accept online payments. It gives you the technical components needed to manage transactions safely. Other payment methods you can use with Medua are &lt;a href="https://medusajs.com/blog/how-we-won-medusa-hackathon-paystack-plugin/" rel="noopener noreferrer"&gt;Paystack&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/plugins/payment/klarna" rel="noopener noreferrer"&gt;Klarna&lt;/a&gt;, and &lt;a href="https://docs.medusajs.com/plugins/payment/paypal" rel="noopener noreferrer"&gt;Paypal&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To add Stripe to your store, you must possess a &lt;a href="https://dashboard.stripe.com/register" rel="noopener noreferrer"&gt;Stripe account&lt;/a&gt;. Like Algolia, you must &lt;a href="https://docs.medusajs.com/plugins/payment/stripe#retrieve-stripes-keys" rel="noopener noreferrer"&gt;retrieve your account's API Keys and secrets&lt;/a&gt; to connect Medusa to your Stripe account.&lt;/p&gt;

&lt;p&gt;In the root directory of the Medusa backend, use the following command to install the Stripe plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add medusa-payment-stripe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you need to add configurations to your stripe plugin. In &lt;code&gt;medusa-config.js&lt;/code&gt;, add the following at the end of the &lt;code&gt;plugins&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`medusa-payment-stripe`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head to the dashboard of your Stripe account to retrieve the API Key and secret key. Then, in your Medusa backend, create &lt;code&gt;.env&lt;/code&gt; if it does not exist and add the Stripe key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_API_KEY=sk_...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add Stripe’s webhook in the &lt;code&gt;.env&lt;/code&gt; file. You can get it by heading to the Stripe dashboard and clicking Add an Endpoint. Medusa backend's endpoint, Stripe webhook, is &lt;code&gt;{BACKEND_URL}/stripe/hooks&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Add this endpoint to its field if you are in production, and replace {BACKEND_URL} with the URL of your backend. Add a description, select at least one action, and then click Add Endpoint. Once the Webhook is created, you’ll see "Signing secret" in the Webhook details. Reveals the secret key by clicking &lt;code&gt;reveal&lt;/code&gt; it and copying it into your Medusa backend.&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_WEBHOOK_SECRET=whsec_...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part of Stripe integration consists of setting it up in the Medusa admin by adding a payment provider.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Settings → &lt;a href="https://docs.medusajs.com/user-guide/regions/manage" rel="noopener noreferrer"&gt;Regions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select a region to edit.&lt;/li&gt;
&lt;li&gt;Click on the icon at the top right of the first section on the right.&lt;/li&gt;
&lt;li&gt;Click on Edit Region Details from the dropdown.&lt;/li&gt;
&lt;li&gt;Under the provider's section, select the payment providers you want to add to the region. Here it's Stripe.&lt;/li&gt;
&lt;li&gt;Unselect the payment providers you want to remove from the region.&lt;/li&gt;
&lt;li&gt;Click Save.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, in your storefront, add the following environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_STRIPE_KEY=&amp;lt;YOUR_PUBLISHABLE_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;&amp;lt;YOUR_PUBLISHABLE_KEY&amp;gt;&lt;/code&gt; corresponds to your Stripe Publishable Key.&lt;/p&gt;

&lt;p&gt;If you run your Medusa storefront and backend, add products to your cart, and then proceed to checkout, you can now use Stripe as a payment method. Here is what the final result looks like: &lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store4-1024x558.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store4-1024x558.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could also check out this &lt;a href="https://medusajs.com/blog/stripe-ecommerce/" rel="noopener noreferrer"&gt;Stripe guide&lt;/a&gt; for more information regarding its integration&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Store
&lt;/h2&gt;

&lt;p&gt;The final step of this tutorial consists of testing the whole store. While the Medusa server is still running, restart your storefront.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;localhost:8000&lt;/code&gt; to see the final electronic commerce store: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F57ce858b-cdcc-4881-ba6c-695d34345578%2Fstore5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F57ce858b-cdcc-4881-ba6c-695d34345578%2Fstore5.png" alt="store5.PNG" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the store item in the navigation bar to open the store:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F86216436-9ea9-48ee-902c-34018524ce26%2Fstore7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F86216436-9ea9-48ee-902c-34018524ce26%2Fstore7.png" alt="store7.PNG" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add some products to your cart:&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store8-1024x467.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store8-1024x467.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Checkout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F34252a44-9c70-4bdb-a9e9-31a3f04b319c%2Fstore9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fe27708d9-808e-4b35-b986-9f26951398de%2F34252a44-9c70-4bdb-a9e9-31a3f04b319c%2Fstore9.png" alt="store9.PNG" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View your order:&lt;/p&gt;

&lt;p&gt;!&lt;a href="https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store10-1024x465.png" rel="noopener noreferrer"&gt;https://www.learn-dev-tools.blog/wp-content/uploads/2023/03/store10-1024x465.png&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, you learned how to build a headless electronic commerce platform with the Medusa engine, using the Next.js Storefront and integrating services like search and payment using Algolia and Stripe.&lt;/p&gt;

&lt;p&gt;However, you can add many other functionalities to this electronic commerce store. Some include gift cards, tax management, &lt;a href="https://docs.medusajs.com/modules/carts-and-checkout/backend/add-fulfillment-provider" rel="noopener noreferrer"&gt;custom shipping methods&lt;/a&gt;, and &lt;a href="https://docs.medusajs.com/plugins/notifications/" rel="noopener noreferrer"&gt;Notifications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Go to the &lt;a href="https://docs.medusajs.com/quickstart/quick-start" rel="noopener noreferrer"&gt;Medusa documentation&lt;/a&gt; for more details regarding these functionalities and services.&lt;/p&gt;

&lt;p&gt;This article was originally published on the &lt;a href="https://www.learn-dev-tools.blog/how-to-build-an-electronic-commerce-store-with-medusa-and-nextjs/" rel="noopener noreferrer"&gt;Learn Dev Tools Blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>beginners</category>
      <category>ecommerce</category>
      <category>webdev</category>
    </item>
    <item>
      <title>7 Useful Mobile Applications to Improve Your Productivity as a Developer</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Tue, 04 Jul 2023 20:37:08 +0000</pubDate>
      <link>https://dev.to/gunkev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer-45p2</link>
      <guid>https://dev.to/gunkev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer-45p2</guid>
      <description>&lt;p&gt;Today many things that were usually done on a desktop, can now be performed on your smartphone thanks to the evolution of Mobile phones. Developers must be always on the move in today's fast-paced world, working on their projects and remaining productive. Mobile apps, which provide ease and flexibility, have become an essential element of a developer's arsenal. Having the appropriate mobile apps may dramatically enhance your productivity, whether you're coding on the move or managing your projects remotely. In this article, we'll look at the best mobile apps that every developer should use.&lt;/p&gt;

&lt;h2 id="heading-1-code-from-anywhere-with-replit"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-1-code-from-anywhere-with-replit" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-1-code-from-anywhere-with-replit"&gt;1. Code from Anywhere with Replit&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501306208%2Fda27ce0c-bc94-44a3-9255-03ed3a6d9ae8.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501306208%2Fda27ce0c-bc94-44a3-9255-03ed3a6d9ae8.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1076" height="2295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coding from anywhere and doing everything you'd normally do on a desktop, such as coding the complete project, sharing, and hosting, are all possible. Everything is handled via your phone. Replit comes with everything you need to get started, including a CPU, storage, and RAM. You can interact with the terminal, use a file system, and do other things. Replit is available for &lt;a href="https://play.google.com/store/apps/details?id=com.replit.app&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;Android&lt;/a&gt; and &lt;a href="https://apps.apple.com/us/app/replit-code-anything/id1614022293" rel="noreferrer noopener"&gt;iOS&lt;/a&gt; systems&lt;/p&gt;

&lt;h2 id="heading-2-improve-your-productivity-with-macrodroid"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-2-improve-your-productivity-with-macrodroid" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-2-improve-your-productivity-with-macrodroid"&gt;2. Improve Your Productivity with Macrodroid&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501316463%2F78debcf0-fe0d-4f1b-aac4-bf9301ce87b3.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501316463%2F78debcf0-fe0d-4f1b-aac4-bf9301ce87b3.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1066" height="2242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One feature that Android consumers like is freedom. They are free to do whatever they choose. You can do even more using Macrodroid. It is one of the best Android device automation tools. You can auto-skip YouTube advertising, auto-swipe in TikTok, do something when something happens, and so on. The app is readily available for &lt;a href="https://play.google.com/store/apps/details?id=com.arlosoft.macrodroid&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;Android&lt;/a&gt; users only&lt;/p&gt;

&lt;h2 id="heading-3-test-your-apis-directly-with-your-mobile-phone-using-api-tester"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-3-test-your-apis-directly-with-your-mobile-phone-using-api-tester" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-3-test-your-apis-directly-with-your-mobile-phone-using-api-tester"&gt;3. Test your APIs directly with Your Mobile Phone using API Tester&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501330688%2F36388484-4dd9-420c-a02b-0907fb89c0a6.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501330688%2F36388484-4dd9-420c-a02b-0907fb89c0a6.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1080" height="2295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;API Tester is a robust tool for testing and debugging APIs from mobile devices. It has most of the functionalities of a typical desktop API testing program. You can test &lt;a href="https://www.learn-dev-tools.blog/creating-a-custom-api-with-strapi/" rel="noreferrer noopener"&gt;any API&lt;/a&gt;, including REST, GraphQL, WebSocket, and SOAP. It uses cURL or files from your device to import requests and collections. You can also run all queries simultaneously, save variables for later use, and integrate with other Widgets. API Tester is available on both &lt;a href="https://apps.apple.com/us/app/replit-code-anything/id1614022293" rel="noreferrer noopener"&gt;AppStore&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=apitester.org&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;PlayStore&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-4-share-your-thoughts-easily-with-mindly"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-4-share-your-thoughts-easily-with-mindly" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-4-share-your-thoughts-easily-with-mindly"&gt;4. Share Your Thoughts Easily with Mindly&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501330688%2F36388484-4dd9-420c-a02b-0907fb89c0a6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501330688%2F36388484-4dd9-420c-a02b-0907fb89c0a6.jpeg" alt="" width="800" height="1700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share your work with friends, coworkers, or team members while maintaining control over viewing and editing rights. Collaborate whether you're at home, in your workplace, or on the other side of the world. With a single click, Mindly can convert your well-structured Word, &lt;a href="https://www.learn-dev-tools.blog/what-is-html-start-learning-web-development/" rel="noreferrer noopener"&gt;HTML&lt;/a&gt;, or Markdown document into a creative and entertaining mind map. Anytime you add, delete, or relocate nodes, your map automatically updates and preserves its layout. You can even import and export to or from your Dropbox account.&lt;/p&gt;

&lt;p&gt;The app is readily available on &lt;a href="https://apps.apple.com/us/app/mindly-mind-mapping/id771221376" rel="noreferrer noopener"&gt;AppStore&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=com.dripgrind.mindly&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;PlayStore&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-5-avoid-painless-configurations-with-expo-go"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-5-avoid-painless-configurations-with-expo-go" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-5-avoid-painless-configurations-with-expo-go"&gt;5. Avoid Painless Configurations with Expo Go&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501341607%2F2bcbb9f5-79bd-4e7f-9315-38863d9bd91f.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501341607%2F2bcbb9f5-79bd-4e7f-9315-38863d9bd91f.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1080" height="2205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expo Go will be extremely beneficial for React Native developers or anyone on their way to want to learn React Native. Rather than installing and configuring Android Studio locally for development. Simply use Expo Go to see real-time changes and easily share your creation with everyone. Create a react-native project with appropriate configurations, then scan the QR code from the console. The Expo Go smartphone app will provide access to the project. All your changes will be directly reflected in the app. You can download the app on &lt;a href="https://play.google.com/store/apps/details?id=host.exp.exponent&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;PlayStore&lt;/a&gt; or &lt;a href="https://apps.apple.com/us/app/expo-go/id982107779" rel="noreferrer noopener"&gt;AppStore&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="heading-6-stay-up-to-date-with-devbytes"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-6-stay-up-to-date-with-devbytes" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-6-stay-up-to-date-with-devbytes"&gt;6. Stay up to date with DevBytes&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501348628%2Fe817137e-07c7-4a7e-938c-7b1d96e4ed0c.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501348628%2Fe817137e-07c7-4a7e-938c-7b1d96e4ed0c.jpeg%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1066" height="2261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a developer, you must constantly stay up to date on the latest technological developments. However, most of you do not have time to investigate and read lengthy articles. DevBytes explores, resumes and presents popular computer programming news. Not only can you obtain news, but you can also learn new coding ideas with explanations and code samples, find career possibilities, and participate anonymously in public polls to express your opinions. The application is only available on &lt;a href="https://play.google.com/store/apps/details?id=com.candelalabs.devbytes&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;PlayStore&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="heading-7-plan-easily-with-planio"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-7-plan-easily-with-planio" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-7-plan-easily-with-planio"&gt;7. Plan Easily with Planio&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501367231%2F8b081135-9c7d-45e6-a45d-56d118b48301.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688501367231%2F8b081135-9c7d-45e6-a45d-56d118b48301.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="1242" height="2208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Planio implements scrum and agile methods. On the Agile Kanban-style board, you can plan sprints and track progress. However, more standard project management tools such as milestones and the Gantt chart can also be used. This is useful when you cannot manage everything using agile methodologies. As your team completes milestones, they'll start coming up with brilliant ideas. You want to debate them, evaluate their merits, and record your team's findings. &lt;a href="https://play.google.com/store/apps/details?id=io.plan.app.cordova&amp;amp;hl=en&amp;amp;gl=US" rel="noreferrer noopener"&gt;Planio&lt;/a&gt; also, allows you to construct your wiki, discuss ideas in a forum atmosphere, or simply leave comments on, well, just about anything. Work from anywhere at any time.&lt;/p&gt;

&lt;h2 id="heading-conclusion"&gt;&lt;a href="https://gunkev.hashnode.dev/7-useful-mobile-applications-to-improve-your-productivity-as-a-developer#heading-conclusion" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Mobile apps have transformed the way developers work and increased their productivity. Developers can code, collaborate, manage projects, and stay connected right from their mobile apps. Their simplicity and flexibility can enable developers to be productive even while they are on the go. Including these top mobile apps in their toolset will certainly improve the development process and yield better results.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>developer</category>
      <category>developertools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Localize Your Vue Application with Tolgee?</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Thu, 29 Jun 2023 12:34:45 +0000</pubDate>
      <link>https://dev.to/gunkev/how-to-localize-your-vue-application-with-tolgee-41aa</link>
      <guid>https://dev.to/gunkev/how-to-localize-your-vue-application-with-tolgee-41aa</guid>
      <description>&lt;p&gt;Nowadays, providing solutions to specific problems identified in a domain has fueled the creation and growth of many companies and startups. But one problem arises which is how to market the product in various places given that not everyone does not have the same background. Even though English is one of the most used languages, especially in the professional domain, this does not exclude the other part of the population that uses other languages. To build an application or solution for a specific area, you need to assure that your solution will &lt;strong&gt;efficiently communicate with the target audience&lt;/strong&gt;. &lt;a href="https://tolgee.io/blog/benefits-challenges-software-localization" rel="noreferrer noopener"&gt;Software localization&lt;/a&gt; is the process of adapting a product or a service offered by the software to meet the needs of a desired culture, population or language.&lt;/p&gt;

&lt;p&gt;In this article, you will learn how to &lt;strong&gt;localize your Vue application in various languages&lt;/strong&gt; with Tolgee an open-source tool. But before that let’s have an overview of what Tolgee and Vuejs are.&lt;/p&gt;

&lt;h2 id="heading-what-is-tolgee"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-what-is-tolgee" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-what-is-tolgee"&gt;What is Tolgee?&lt;/h2&gt;

&lt;p&gt;Tolgee is an &lt;strong&gt;open-source platform&lt;/strong&gt; that provides a user-friendly interface for organizing translations for developers and translators. It facilitates the translation process by providing a centralized site for all translation duties, such as managing translation files, translating text, and integrating with popular development tools &lt;strong&gt;such as Nextjs, Gatsby or Angular.&lt;/strong&gt; Tolgee also provides a number of tools to aid &lt;a href="https://tolgee.io/features/collaboration" rel="noreferrer noopener"&gt;collaborative translation operations&lt;/a&gt;, including translation memory. Tolgee is an &lt;strong&gt;all-in-one solution&lt;/strong&gt; for project management and optimizing multilingual content distribution.&lt;/p&gt;

&lt;h2 id="heading-what-is-vuejs"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-what-is-vuejs" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-what-is-vuejs"&gt;What is Vue.js?&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vuejs.org" rel="noreferrer noopener"&gt;Vue.js&lt;/a&gt; is a JavaScript framework used for creating user interfaces, PWA and single-page apps. It is intended to be adopted in stages and can also serve as a web application framework capable of powering complex single-page applications. Vue.js is well-known for its ease of use and simplicity, making it a popular choice among developers for creating scalable and performant web apps.&lt;/p&gt;

&lt;h2 id="heading-how-to-integrate-tolgee-into-a-vue-application"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-how-to-integrate-tolgee-into-a-vue-application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;h2 id="heading-how-to-integrate-tolgee-into-a-vue-application"&gt;How to Integrate Tolgee into a Vue Application&lt;/h2&gt;

&lt;p&gt;This selection highlights a step-by-step approach to integrating Togee into a Vue application.&lt;/p&gt;

&lt;h3 id="heading-step-1-install-tolgee"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-1-install-tolgee" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-1-install-tolgee"&gt;Step 1: Install Tolgee&lt;/h3&gt;

&lt;p&gt;You can either create a Tolgee project via the &lt;a href="https://app.tolgee.io/login" rel="noreferrer noopener"&gt;Tolgee Cloud Platform&lt;/a&gt; or self-host it. In this tutorial, we will use the hosted version of Tolgee. However, you can host the server on your own via Docker. If you don’t have Docker installed use this Docker &lt;a href="https://docs.docker.com/desktop/" rel="noreferrer noopener"&gt;quick guide&lt;/a&gt; to get started&lt;/p&gt;

&lt;p&gt;It is recommended to run it in a single container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;docker run -v tolgee_data:/data/ -p 8085:8080 tolgee/tolgee
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then follow &lt;a href="https://tolgee.io/platform/self_hosting/running_with_docker" rel="noreferrer noopener"&gt;this guide&lt;/a&gt; to configure your Tolgee project.&lt;/p&gt;

&lt;h3 id="heading-step-2-create-a-project"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-2-create-a-project" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-2-create-a-project"&gt;Step 2: Create a Project&lt;/h3&gt;

&lt;p&gt;Once everything is set up, log into your account and create a new project. Give a name to your project and &lt;strong&gt;add the languages&lt;/strong&gt; you wish your app to use. In this tutorial, the app will use three languages: English, French and Spanish&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2Axr9RL729DkAd6Dfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2Axr9RL729DkAd6Dfb.png" alt="" width="700" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="heading-step-3-generate-api-key"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-3-generate-api-key" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-3-generate-api-key"&gt;Step 3: Generate API Key&lt;/h3&gt;

&lt;p&gt;Still, in your Tolgee dashboard, navigate to &lt;code&gt;integrate&lt;/code&gt;; the last element in the left menu and select Vue among the list of app integrations&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2ADdnpqhKL4MkmfVv-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2ADdnpqhKL4MkmfVv-.png" alt="" width="700" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then in the select input, click on &lt;code&gt;Create new&lt;/code&gt; to create a new API Key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2ALtvjIf230R4oWbao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2ALtvjIf230R4oWbao.png" alt="" width="700" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name your API key and select the period of time you wish to use it and click on Save.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AloWX2fNR1L3um1es.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AloWX2fNR1L3um1es.png" alt="" width="700" height="820"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will generate a first-time-only token that you need to save somewhere on your PC because you will use it later.&lt;/p&gt;

&lt;h3 id="heading-step-4-add-tolgee-to-your-vue-application"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-4-add-tolgee-to-your-vue-application" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-4-add-tolgee-to-your-vue-application"&gt;Step 4: Add Tolgee to Your Vue application&lt;/h3&gt;

&lt;p&gt;Navigate to your desired directory and run the following command :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;npm init vue@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will be prompted to provide your project’s name and other information.&lt;/p&gt;

&lt;p&gt;If you already have a project setup, you can skip this step.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Vue.js - The Progressive JavaScript Framework
√ Project name: ... vue-tolgee
√ Add TypeScript? ... No / Yes
√ Add JSX Support? ... No / Yes
√ Add Vue Router for Single Page Application development? ... No / Yes
√ Add Pinia for state management? ... No / Yes
√ Add Vitest for Unit Testing? ... No / Yes
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No / Yes
√ Add Prettier for code formatting? ... No / Yes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the command is executed, navigate to the project’s root directory and run &lt;code&gt;npm install&lt;/code&gt;, then &lt;code&gt;npm run dev&lt;/code&gt; to start the app.&lt;/p&gt;

&lt;p&gt;If your application does not automatically open, type &lt;code&gt;[&lt;/code&gt;&lt;a href="https://gunkev.hashnode.dev/%20raw%20`[`%20endraw%20http://localhost:5173%20raw%20`](http://localhost:5173)`%20endraw%20" rel="noreferrer noopener"&gt;&lt;code&gt;localhost:5173&lt;/code&gt;](&lt;/a&gt;&lt;a href="http://localhost:5173" rel="noreferrer noopener"&gt;localhost:5173&lt;/a&gt;)`` in your browser to visualise your freshly created application.&lt;/p&gt;

&lt;p&gt;If your project is a Vue 3 Typescript project create &lt;code&gt;shims-vue.d.ts&lt;/code&gt; file and add the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/* eslint-disable */
declare module '*.vue' {
  import type { DefineComponent } from 'vue';
  const component: DefineComponent&amp;lt;{}, {}, any&amp;gt;;
  export default component;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will allow you to import vue components.&lt;/p&gt;

&lt;h3 id="heading-step-5-install-tolgee-package"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-5-install-tolgee-package" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-5-install-tolgee-package"&gt;Step 5: Install Tolgee Package&lt;/h3&gt;

&lt;p&gt;Now use the command below to install Tolgee.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd vue-tolgee
npm install @tolgee/vue
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="heading-step-6-add-environment-variable"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-6-add-environment-variable" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-6-add-environment-variable"&gt;Step 6: Add Environment Variable:&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;env.development.local&lt;/code&gt; file if you don’t have one at the root of your project and paste the following code&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VUE_APP_TOLGEE_API_URL=&amp;lt;host&amp;gt;
VUE_APP_TOLGEE_API_KEY=&amp;lt;Only new key can be revealed&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;VUE_APP_TOLGEE_API_URL&lt;/code&gt; is where your Tolgee server is hosted&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VUE_APP_TOLGEE_API_KEY&lt;/code&gt; is your API key. If somehow you lost the API key you generated earlier, you can regenerate a new one.&lt;/p&gt;

&lt;p&gt;Click on your profile picture at the top right corner of the dashboard&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2A3Xs9Q3NXevRUiEol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2A3Xs9Q3NXevRUiEol.png" alt="" width="700" height="1141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;Select Project API Keys&lt;/code&gt;. In the new window that opens, either you add a new API key or you generate a new one. Make sure to save the API key somewhere this time in case you failed to do it previously.&lt;/p&gt;

&lt;p&gt;Once your API key is ready, you can now add it to your env file.&lt;/p&gt;

&lt;h3 id="heading-step-7-initialize-tolgee"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-7-initialize-tolgee" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-7-initialize-tolgee"&gt;Step 7: Initialize Tolgee&lt;/h3&gt;

&lt;p&gt;Update your &lt;code&gt;main.ts&lt;/code&gt; found at the root of your project with the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import './assets/main.css';
import { createApp } from 'vue';
import App from './App.vue';
import {
  BackendFetch,
  DevTools,
  Tolgee,
  FormatSimple,
  VueTolgee,
} from '@tolgee/vue';

const tolgee = Tolgee()
  .use(DevTools())
  .use(FormatSimple())
  .use(BackendFetch())
  .init({
    language: 'en',
    apiUrl: import.meta.env.VITE_TOLGEE_API_URL,
    apiKey: import.meta.env.VITE_TOLGEE_API_KEY,
  });
const app = createApp(App);
app.use(VueTolgee, { tolgee });
app.mount('#app');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then wrap your code in &lt;code&gt;src/app.vue&lt;/code&gt; in &lt;code&gt;TolgeeProvider&lt;/code&gt; tag like below:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;TolgeeProvider&amp;gt;
    &amp;lt;header&amp;gt;
      &amp;lt;img
        alt="Vue logo"
        class="logo"
        src="./assets/logo.svg"
        width="125"
        height="125"
      /&amp;gt;
      &amp;lt;div class="wrapper"&amp;gt;
        &amp;lt;HelloWorld msg="You did it!" /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/header&amp;gt;
    &amp;lt;main&amp;gt;
      &amp;lt;TheWelcome /&amp;gt;
    &amp;lt;/main&amp;gt;
  &amp;lt;/TolgeeProvider&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="heading-step-8-add-translations"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-8-add-translations" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-8-add-translations"&gt;Step 8: Add Translations&lt;/h3&gt;

&lt;p&gt;Now head to your Tolgee server and add some translations. In this tutorial, you will be translating the default Vue 3 starting application so for each text, add a keyname, and add the default English text. Make sure to &lt;a href="https://tolgee.io/blog/naming-translation-keys" rel="noreferrer noopener"&gt;conventionally name your translations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AFItDSCk2-zF997h8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AFItDSCk2-zF997h8.png" alt="" width="700" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click on the French and Spanish input to select a translation from the &lt;a href="https://tolgee.io/blog/tolgee-ai-translator" rel="noreferrer noopener"&gt;machine-generated translations&lt;/a&gt; that best describes the text. You can adjust if you feel that the text does not fit its context.&lt;/p&gt;

&lt;p&gt;Repeat this process until all translations are added. In the end, you should see the list of translations below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2Ac3Efn7wpzP69xDNk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2Ac3Efn7wpzP69xDNk.png" alt="" width="700" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id="heading-step-9-create-a-select-component"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-9-create-a-select-component" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-9-create-a-select-component"&gt;Step 9: Create a Select Component&lt;/h3&gt;

&lt;p&gt;The select component will help you easily switch from one language to another. In your &lt;code&gt;src/components&lt;/code&gt; created &lt;code&gt;selector.vue&lt;/code&gt; file and add the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script setup&amp;gt;
import { useTolgee } from '@tolgee/vue'
const tolgee = useTolgee(['language'])
const changeLanguage = (e) =&amp;gt; {
  tolgee.value.changeLanguage(e.target.value)
}
&amp;lt;/script&amp;gt;
&amp;lt;template&amp;gt;
  &amp;lt;select :value="tolgee.getLanguage()" v-on:change="changeLanguage" class="selector"&amp;gt;
    &amp;lt;option value="en"&amp;gt;🇬🇧 English&amp;lt;/option&amp;gt;
    &amp;lt;option value="fr"&amp;gt;🇫🇷 Français&amp;lt;/option&amp;gt;
    &amp;lt;option value="es"&amp;gt;🇪🇸 Español&amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let’s give some style to the select tag with the following code in &lt;code&gt;src/assets.main.css&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.selector {
  display: block;
  text-align: center;
  width: 80%;
  padding: 10px;
  border-radius: 5px;
  font-size: 18px;
  border: none;
  background: rgba(0, 255, 255, 0.51);
  -webkit-appearance: none;
  -moz-appearance: none;
  text-indent: 1px;
  text-overflow: '';
  cursor: pointer;
  margin-left: 10px;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="heading-step-10-add-translation-keynames"&gt;&lt;a href="https://gunkev.hashnode.dev/how-to-localize-your-vue-application-with-tolgee#heading-step-10-add-translation-keynames" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/h3&gt;

&lt;h3 id="heading-step-10-add-translation-keynames"&gt;Step 10: Add Translation Keynames&lt;/h3&gt;

&lt;p&gt;This is the last step which consists of adding translation keynames. A typical translation looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import { T } from '@tolgee/vue';

&amp;lt;T keyName="documentation" defaultValue="Documentation" /&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So in your &lt;code&gt;src/components/TheWelcome.vue&lt;/code&gt; replace your code with the following:&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;src/components/HelloWorld.vue&lt;/code&gt; add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script setup lang="ts"&amp;gt;
import { T } from '@tolgee/vue'
...
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div class="greetings"&amp;gt;
   ...
    &amp;lt;h3&amp;gt;
      &amp;lt;T keyName="success" defaultValue="You've successfully created a project with Vite + Vue 3" /&amp;gt;
    &amp;lt;/h3&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, update your &lt;code&gt;App.vue&lt;/code&gt; file with the code below:&lt;/p&gt;

&lt;p&gt;In the code block above, we have updated our components imports and added the &lt;code&gt;Selector&lt;/code&gt; component we created earlier.&lt;/p&gt;

&lt;p&gt;But there is one last thing we need to do, which is to add our translation files to the Vue project.&lt;/p&gt;

&lt;p&gt;In your Tolgee dashboard, click on export in the left menu, then select the languages you wish to use and click on export.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AGh2Nh66jn-M2YRBj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AGh2Nh66jn-M2YRBj.png" alt="" width="700" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create i18n folder in your Vue application public folder and add the extracted files&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AqUu9223BpJ7mwSSy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AqUu9223BpJ7mwSSy.png" alt="" width="700" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;utilis/i18n/en.json&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "documentation": "Documentation",
  "documentation_desc": "Vue’s official documentation provides you with all information you need to get started.",
  "tooling": "Tooling",
  "tooling_desc": "This project is served and bundled with Vite. The recommended IDE setup is VSCode + Volar. If you need to test your components and web pages, check out Cypress and Cypress Component Testing. More instructions are available in README.md.",
  "ecosystem": "Ecosystem",
  "ecosystem_desc": "Get official tools and libraries for your project: Pinia, Vue Router, Vue Test Utils, and Vue Dev Tools. If you need more resources, we suggest paying Awesome Vue a visit. ",
  "community": "Community",
  "community_desc": "Got stuck? Ask your question on Vue Land, our official Discord server, or StackOverflow. You should also subscribe to our mailing list and follow the official @vuejs twitter account for latest news in the Vue world. ",
  "support Vue": "Support Vue",
  "support Vue_desc": "As an independent project, Vue relies on community backing for its sustainability. You can help us by becoming a sponsor. "
    "readme": "More instructions are available in README.md.",
  "success": "You’ve successfully created a project with Vite + Vue 3",
  "hello": "Hello"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;utilis/i18n/fr.json&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "documentation": "Documentación",
  "documentation_desc": "La documentation officielle de Vue vous fournit toutes les informations dont vous avez besoin pour commencer.",
  "tooling": "Outillage",
  "tooling_desc": "Ce projet est servi et groupé avec Vite. La configuration IDE recommandée est VSCode + Volar. Si vous avez besoin de tester vos composants et vos pages Web, consultez Cypress et Cypress Component Testing. Plus d'instructions sont disponibles dans README.md.",
  "ecosystem": "Écosystème",
  "ecosystem_desc": "Obtenez des outils et des bibliothèques officiels pour votre projet : Pinia, Vue Router, Vue Test Utils et Vue Dev Tools. Si vous avez besoin de plus de ressources, nous vous suggérons de rendre visite à Awesome Vue.",
  "community": "Communauté",
  "community_desc": "Est resté coincé? Posez votre question sur Vue Land, notre serveur Discord officiel, ou StackOverflow. Vous devez également vous inscrire à notre liste de diffusion et suivre le compte Twitter officiel @vuejs pour les dernières nouvelles du monde Vue.",
  "support Vue": "Soutenir Vue",
  "support Vue_desc": "En tant que projet indépendant, Vue s'appuie sur le soutien de la communauté pour sa durabilité. Vous pouvez nous aider en devenant parrain.",
  "readme": "Plus d'instructions sont disponibles dans README.md.",
  "success": "Vous avez réussi à créer un projet avec Vite + Vue 3",
  "hello": "Bonjour"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;utilis/i18n/es.json&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  "documentation": "Documentación",
  "documentation_desc": "La documentación oficial de Vue le brinda toda la información que necesita para comenzar.",
  "tooling": "Estampación",
  "tooling_desc": "Este proyecto se sirve y se incluye con Vite. La configuración IDE recomendada es VSCode + Volar. Si necesita probar sus componentes y páginas web, consulte Cypress y Cypress Component Testing. Más instrucciones están disponibles en README.md.",
  "ecosystem": "Ecosistema",
  "ecosystem_desc": "Obtenga herramientas y bibliotecas oficiales para su proyecto: Pinia, Vue Router, Vue Test Utils y Vue Dev Tools. Si necesita más recursos, le sugerimos que visite Awesome Vue. ",
  "community": "Comunidad",
  "community_desc": "¿Quedó atascado? Haga su pregunta en Vue Land, nuestro servidor oficial de Discord o StackOverflow. También debe suscribirse a nuestra lista de correo y seguir la cuenta oficial de Twitter @vuejs para conocer las últimas noticias en el mundo de Vue. ",
  "support Vue": "Soporte Vue",
  "support Vue_desc": "Como proyecto independiente, Vue depende del respaldo de la comunidad para su sostenibilidad. Puedes ayudarnos convirtiéndote en patrocinador. ",
  "readme": "Hay más instrucciones disponibles en README.md.",
  "success": "Ha creado con éxito un proyecto con Vite + Vue 3",
  "hello": "Hola"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you re-run your application, it should be working perfectly like the screenshot below:&lt;/p&gt;

&lt;p&gt;You can find the full working code in this &lt;a href="https://github.com/Gunkev/vue-tolgee/tree/main" rel="noreferrer noopener"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>beginners</category>
      <category>translation</category>
    </item>
    <item>
      <title>What is Open Source Software? A Beginner’s Guide</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Thu, 29 Jun 2023 12:27:56 +0000</pubDate>
      <link>https://dev.to/gunkev/what-is-open-source-software-a-beginners-guide-5o9</link>
      <guid>https://dev.to/gunkev/what-is-open-source-software-a-beginners-guide-5o9</guid>
      <description>&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;If you have been in the tech industry for a while you may have heard about open source or open source software. When we talk about open source, you should think of open collaboration, open exchange, participation transparency and community-oriented development.&lt;/p&gt;

&lt;p&gt;In this article, you will learn more about open source software including the difference between open source and other forms of software, key features of OSS, pros and cons of open source, how to start contributing to open source and some examples of OSS.&lt;/p&gt;

&lt;h2&gt;What is Open Source?&lt;/h2&gt;

&lt;p&gt;Open source designed any program whose source code can be modified and shared by anyone because it’s openly accessible.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;What is Source Code?&lt;/h2&gt;

&lt;p&gt;A source code is a set of instructions written in the form of functions, descriptions, methods, calls or any other operational statement by a programmer using a computer programming language. A source code can be open that is available to anyone to edit and redistribute or closed. Source code is the part that most software or computer users cannot see.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.learn-dev-tools.blog%2Fwp-content%2Fuploads%2F2023%2F03%2Fsource-code-medusa-js-github-1024x567.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.learn-dev-tools.blog%2Fwp-content%2Fuploads%2F2023%2F03%2Fsource-code-medusa-js-github-1024x567.png" alt="Example of a Source code taken from Medusa's GitHub Repository " width="800" height="442"&gt;&lt;/a&gt;Example of a Source code taken from Medusa's GitHub Repository &lt;p&gt;&lt;/p&gt;



&lt;h2&gt;What Does Open Source Software(OSS) Mean?&lt;/h2&gt;

&lt;p&gt;The term Open source software is a non-proprietary software in which code is publicly available to anyone to inspect, comment on, modify, improve and redistribute it. Open source software is created and maintained by a group of developers so if you are new to computer programming, you can begin your journey as an open source contributor.&lt;/p&gt;

&lt;p&gt;I have known open source since 2017 and started with my first contribution in 2020. I made my first contributions to the &lt;a href="https://github.com/apache" rel="noreferrer noopener"&gt;Apache Software Foundation&lt;/a&gt; Gihhub and &lt;a href="https://github.com/mozilla-mobile" rel="noreferrer noopener"&gt;Mozilla mobile Github&lt;/a&gt;.&amp;nbsp;&lt;/p&gt;

&lt;h2&gt;What is the Difference between Open source Software and other Types of Software?&lt;/h2&gt;

&lt;p&gt;Do not get confused with open source, free, closed software and freeware. This section highlights the differences between these types of Software.&lt;/p&gt;

&lt;h3&gt;Open Source vs Free Software&lt;/h3&gt;

&lt;p&gt;Open source software is software available for anyone to edit, improve and redistribute. The source code is accessible to anyone. Open source promotes collaboration and sharing of source code. It’s an effective way for organizations, developers and individuals to collaborate. Freedom to use the software is not absolute or imposed but it’s allowed. Every open source software is not free. However, some open source licenses are restrictive. &lt;strong&gt;WordPress&lt;/strong&gt; is an example of open source software&lt;/p&gt;

&lt;p&gt;Free Software describes software in which source code is free to use, edit and redistribute without any restrictions. With free software, Freedom is a value that is more important than any other thing. All free software is open source software. &lt;strong&gt;Ares is an example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open source and free software have distinguished features. Some open source software is restrictive and all free software are open source software but not all open source software is free software.&amp;nbsp;&lt;/p&gt;

&lt;h3&gt;Open Source Software vs Freeware&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.learn-dev-tools.blog/what-is-a-free-tool-all-you-should-know/" rel="noopener noreferrer"&gt;Freeware&lt;/a&gt;&lt;/strong&gt; is any software that is free to use and available to the general public. If you are a, it can be frustrating to use freeware because its source code is not open or rather, it’s closed so you can edit the software to fit your needs. You can only use the features it offers even if it it is limited. Google Chrome is an example of freeware&lt;/p&gt;

&lt;p&gt;While with open source software, you have the possibility to edit, improve, and add different features to fit your needs. An example is Chromium.&lt;/p&gt;

&lt;p&gt;Freeware source code is closed, only the developer or organization who created the software can view its source code while open source one can edit and redistribute. Freeware is copyrighted while Open source is not.&lt;/p&gt;

&lt;h3&gt;Open Source vs Proprietary Software&lt;/h3&gt;

&lt;p&gt;You do not need an authenticated license to use open source software. They function with GNU Public License. They can be installed freely on any computer system. Open source software is developed and maintained through open collaboration.&lt;/p&gt;

&lt;p&gt;Proprietary Software is software with protected source code(closed). They cannot be installed on a computer without a valid license. So users need a valid and authenticated license to use it. They are also copyrighted like freeware but the difference is that you do not need to pay to use freeware. An example is Windows&lt;/p&gt;

&lt;h2&gt;Key features of Open Source Software&lt;/h2&gt;

&lt;p&gt;In this section, I will elaborate on the various key features and values that cover Open source Software.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transparency: &lt;/strong&gt;Open source allows you to track and understand any changes made to software without relying on the software manufacturer.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Collaboration&lt;/strong&gt;: The main goal of the open source initiative is to allow anyone to freely participate and improve the software. By working together, we can easily identify and solve problems&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Meritocracy&lt;/strong&gt;: Including diverse perspectives from different brains can help identify the best ideas by decision-makers and could equally determine the success of a project.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Community&lt;/strong&gt;: When many people gather to help and share ideas, it creates a community where each other can get support.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Review&lt;/strong&gt;: Since source code is freely available to the public, different experimented community members can check, propose ideas and improve the software.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Many collaborators can update and test the code to check if there is any bug and fix it. Open sources do not depend on one, two or three people to maintain but rather have a large community that supports them.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt;: Flexibility refers to the fact that you can use open source code to address problems that are unique to your business. Plus you can rely on community help to improve an existing solution or implement a new one.&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Lower cost:&lt;/strong&gt; Most of the time, open source is free to use and download. Sometimes you may need to pay for only reinforcing security and interoperability.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;What are the Benefits of Open Source?&lt;/h2&gt;

&lt;p&gt;Choosing to use or contribute to open source software offers many benefits. Some of them include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open source is free&lt;/li&gt;



&lt;li&gt;You have more control over the software. Compare to Closed software you can edit open source software to fit your needs.&lt;/li&gt;



&lt;li&gt;It's good for anyone beginning computer programming. As a beginner, you can become a better programmer by sharing your work and contributing to other works&lt;/li&gt;



&lt;li&gt;Open source is good because it can help to enhance software security as anyone can spot and fix a security issue quickly that the original programmer may have missed&lt;/li&gt;



&lt;li&gt;Open source projects tend to be more stable than other types of software since there is a great community behind ready-to-deploy means to improve it.&lt;/li&gt;



&lt;li&gt;It promotes collaboration through the creation of communities.&lt;/li&gt;



&lt;li&gt;Open source is flexible&lt;/li&gt;



&lt;li&gt;They have Good &lt;strong&gt;Documentation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;What are the Disadvantages of Open Source Software?&lt;/h2&gt;

&lt;p&gt;Not everyone embraces the open source philosophy. Some organizations still debate the fact that open source software is more vulnerable since their source code is open to anyone.&lt;/p&gt;

&lt;p&gt;I have been in the software community for 3 to 4 years now and I have noticed that they tend to be more secure since issues linked to security or not can easily be identified and fixed by community members. However, I equally notice some drawbacks of open source.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open source software can be difficult to set up, especially for beginners. Some of them may lack user-friendly interfaces imposing you to use a &lt;strong&gt;Terminal&lt;/strong&gt;
&lt;/li&gt;



&lt;li&gt;Liability problems. Open source rarely contains warrant or liability protections&lt;/li&gt;



&lt;li&gt;Compatibility issues.&amp;nbsp;&lt;/li&gt;



&lt;li&gt;Initially, there is no cost but it can be costly in the long term especially if you need immediate support. There is also training cost in case you incorporate the software into your team.&lt;/li&gt;



&lt;li&gt;Not all open source possesses a user manual although you can seek help from the community it won't always be available immediately like commercial software where support is available 24h/7&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;How can you Start Contributing to Open Source?&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.learn-dev-tools.blog%2Fwp-content%2Fuploads%2F2023%2F03%2FHOW-TO-CONTRIBUTE-TO-OSS-1024x576.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.learn-dev-tools.blog%2Fwp-content%2Fuploads%2F2023%2F03%2FHOW-TO-CONTRIBUTE-TO-OSS-1024x576.webp" alt="how to contribute to OSS" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;It’s quite simple to contribute to open source software. You could do that by identifying and fixing bugs, adding features, improving documentation and &lt;strong&gt;&lt;a href="https://www.learn-dev-tools.blog/what-is-content-writing-an-easy-beginners-guide/" rel="noopener noreferrer"&gt;writing content&lt;/a&gt;&lt;/strong&gt; in the form of blog posts, technical tutorials or creating Videos. You also create your own open source project to promote open source.&lt;/p&gt;

&lt;p&gt;To contribute to existing open source software, you should head to &lt;strong&gt;&lt;a href="http://github.com" rel="noreferrer noopener"&gt;GitHub&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find a project and Fork&lt;/li&gt;



&lt;li&gt;Clone the Project to your Local machine&lt;/li&gt;



&lt;li&gt;Create a new branch&lt;/li&gt;



&lt;li&gt;Make changes&lt;/li&gt;



&lt;li&gt;Commit your changes&lt;/li&gt;



&lt;li&gt;Push your changes to your remote repository&lt;/li&gt;



&lt;li&gt;Finally, Create a pull request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don’t have any project ideas, then open source projects to contribute to include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/medusajs/medusa" rel="noreferrer noopener"&gt;Medusa&lt;/a&gt;&amp;nbsp;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/ToolJet/ToolJet" rel="noreferrer noopener"&gt;Tooljet&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/doczjs/docz" rel="noreferrer noopener"&gt;Docz&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/boxyhq/jackson" rel="noreferrer noopener"&gt;SAML Jackson&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://github.com/chatwoot/chatwoot" rel="noreferrer noopener"&gt;Chatwoot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Open Source License&lt;/h2&gt;

&lt;p&gt;An Open source License governs how other individuals besides the initial developer can use, modify and redistribute the software. It allows the software to be transparent and changed by members of a community or third party. According to the &lt;a href="https://opensource.org/licenses/" rel="noreferrer noopener"&gt;Open Source Initiative&lt;/a&gt; include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opensource.org/license/apache-2-0/" rel="noreferrer noopener"&gt;Apache License, Version 2.0&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://opensource.org/license/epl-2-0/" rel="noreferrer noopener"&gt;Eclipse Public License version 2.0&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://opensource.org/license/cddl-1-0/" rel="noreferrer noopener"&gt;Common Development and Distribution License 1.0&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://opensource.org/license/lgpl-2-0/" rel="noreferrer noopener"&gt;GNU Library General Public License version 2&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://opensource.org/license/mpl-2-0/" rel="noreferrer noopener"&gt;Mozilla Public License 2.0 (MPL-2.0)&lt;/a&gt;&lt;/li&gt;



&lt;li&gt;&lt;a href="https://opensource.org/license/bsd-2-clause/" rel="noreferrer noopener"&gt;The 2-Clause BSD License&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Example of Open Source Software&lt;/h2&gt;

&lt;p&gt;You might have used an open source software without realising. There are many of them out there. Some popular and most-used open source software is&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;WordPress, an open source CMS for Content Management&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.learn-dev-tools.blog/what-is-the-flutter-framework-a-beginners-introduction-to-flutter/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;&lt;/strong&gt; for web, desktop and mobile development&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.learn-dev-tools.blog/what-is-a-css-framework-all-you-need-to-know/" rel="noopener noreferrer"&gt;CSS Frameworks&lt;/a&gt;&lt;/strong&gt; Like Bootstrap for website layout&lt;/li&gt;



&lt;li&gt;VLC Media Player for video and sound player&lt;/li&gt;



&lt;li&gt;Libre Office for word processing&lt;/li&gt;



&lt;li&gt;Open source Linux Operating System&lt;/li&gt;



&lt;li&gt;
&lt;strong&gt;Python&lt;/strong&gt; is open source too.&lt;/li&gt;



&lt;li&gt;Open source reporting tools include Metabase, BIRT&lt;/li&gt;



&lt;li&gt;Open source PDF viewer include Qoppa PDF Studio, pdfFiller, and Soda PDF&lt;/li&gt;



&lt;li&gt;Some Java open source projects are Arduino, Mindustry, and Signal Android&lt;/li&gt;



&lt;li&gt;We equally have open source development tools like Eclipse IDE, and Apache Cordova&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;FAQ&lt;/h2&gt;

&lt;h3&gt;Is Open Source Software Free?&lt;/h3&gt;

&lt;p&gt;Most open source software is free but there are some exceptions. Some open source licenses are restrictive and do not allow the creation of a modified version. An example is Open Source Watcom&lt;/p&gt;

&lt;h3&gt;Where can I find OSS?&lt;/h3&gt;

&lt;p&gt;You can find and download open source projects on SourceForge, OSDN, FossHub, GitHub, and F-Droid&lt;/p&gt;

&lt;h3&gt;Why Should you Contribute to OSS?&lt;/h3&gt;

&lt;p&gt;The most important advantage of contributing to open source is that you can find and collaborate with other developers hence improving your developer network.&lt;/p&gt;

&lt;h3&gt;Why is Open Source Important?&lt;/h3&gt;

&lt;p&gt;Open source improves innovations through collaboration with other organizations and developer&lt;/p&gt;

&lt;h3&gt;What are Some Open Source Competitions?&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Open Source Competitions&lt;/strong&gt; are contests where you can learn to participate in open source projects, demonstrate your coding skills, and get feedback. Examples of Open source competitions include Open Source Contest, &lt;a href="https://summerofcode.withgoogle.com/" rel="noreferrer noopener"&gt;Google Summer of Code (GSoC)&lt;/a&gt;, &lt;a href="https://www.outreachy.org/" rel="noreferrer noopener"&gt;Outreachy&lt;/a&gt;, &lt;a href="https://osoc.be/" rel="noreferrer noopener"&gt;Open Summer of Code&lt;/a&gt;, &lt;a href="https://www.fsf.org/volunteer/internships" rel="noreferrer noopener"&gt;Free Software Foundation (FSF) Internship Program&lt;/a&gt;, &lt;a href="https://gssoc.girlscript.tech/" rel="noreferrer noopener"&gt;GirlScript Summer of Code (GSSoC)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;What is Closed Source Software?&lt;/h3&gt;

&lt;p&gt;Closed Software is software whose source code is only available to the author. So only the author can modify and copy it.&lt;/p&gt;

&lt;h3&gt;What is the Open Source Initiative?&lt;/h3&gt;

&lt;p&gt;The Open Source Initiative is a non-profit organisation with the main aim to educate about, form, and inform the benefit of open source&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Open source is available for anyone to edit, use and redistribute. Some key features of open source include transparency, reliability, available community and flexibility. However, OSS can be costly in a long run. So if you are planning to incorporate OSS into your business you need to include additional or urgent costs in your project plan including the cost of training staff members to use the software. Some popular Open source projects include Apache, WordPress, and Mozilla Firefox.&lt;/p&gt;

&lt;p&gt;If you like this kind of content, feel free to share or subscribe to our newsletter below to get fresh content like this periodically. But if you have any questions, you can reach me via &lt;a href="https://twitter.com/gunkev11oz" rel="noreferrer noopener"&gt;my twitter account&lt;/a&gt;. I will be glad to hear from you.&lt;/p&gt;



</description>
      <category>opensource</category>
      <category>softwareengineering</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Let's Flutter Together Today</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Thu, 06 Apr 2023 15:00:06 +0000</pubDate>
      <link>https://dev.to/gunkev/lets-flutter-together-today-3d9d</link>
      <guid>https://dev.to/gunkev/lets-flutter-together-today-3d9d</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/es/@ruizra?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Randall  Ruiz&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/flutter?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nowadays mobile application is growing exponentially. Many people use smartphones these days either for automation, relaxation,  GPS for directions, keep track of appointments and contacts. In fact, many employees and businesses have trouble just imagining a day without a smartphone or mobile device.  As people evolve over time so are the different tools used to build most software found in smartphones. Please note that Flutter is also used to build web and desktop apps. Here I will only focus on mobile applications. Then What is Flutter- Why should you learn Flutter in 2023?&lt;/p&gt;

&lt;p&gt;In this article, I will briefly introduce Flutter to you and list some companies or apps which use or build applications using it.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://flutter.dev" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; is an &lt;a href="https://www.learn-dev-tools.blog/what-does-open-source-software-mean-a-beginners-guide/" rel="noopener noreferrer"&gt;Open source&lt;/a&gt; toolkit used to build expressively beautiful, natively compiled applications in a single code base. One of its benefits and why I will encourage you to use it in your project is the fact that you can build amazing UI and fancy animations. You also have the possibility to build an app that will run on different operating systems. The factor is commonly known as cross-platform. But apart from this why should you learn Flutter in 2023?&lt;/p&gt;

&lt;h2&gt;Fast Development with Flutter&lt;/h2&gt;

&lt;p&gt;Flutter provides a feature known as hot reload which helps you to quickly build and test your UI as well as fix some bugs related to them without having to re-run the whole app. Flutter framework automatically rebuilds the widget tree in order for you to quickly view the effects of any changes.&lt;/p&gt;

&lt;h2&gt;Native Performance&lt;/h2&gt;

&lt;p&gt;Flutter’s widgets provide most of the native components such as scrolling, navigation, icons and fonts to provide full native performance on both iOS and Android.&lt;/p&gt;

&lt;h2&gt;Flutter, a Wonderful Documentation&lt;/h2&gt;

&lt;p&gt;Even if you are new to mobile development or not flutter offers highly detailed and comprehensive &lt;a href="https://www.learn-dev-tools.blog/an-easy-overview-of-software-documentation/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to help developers or none developers acquire the knowledge needed to perform a particular task.&lt;/p&gt;

&lt;h2&gt;Ready-to-use tutorials&lt;/h2&gt;

&lt;p&gt;Truly if you are not still satisfied with the documentation you can go ahead and download tutorials on Udacity, Udemy or any other platform which offers complete mastery tools. These tutorials may help you polish up your skills since they usually contain practical projects.&lt;/p&gt;

&lt;h2&gt;Learn from Developers&lt;/h2&gt;

&lt;p&gt;Learning does not imply learning on your own. There are many developers with outstanding experience as well as forums such as Stack Overflow or GitHub forums to help. Just be humble to yourself and accept to receive and share knowledge with others.&lt;/p&gt;

&lt;h2&gt;Who is using Flutter?&lt;/h2&gt;

&lt;p&gt;I forget to mention it but Google is actually the one who is behind the Flutter framework and many companies around you may know actually use it. Some of those companies are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://google.com" rel="noopener noreferrer"&gt;Google&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Groupon,&lt;/li&gt;
&lt;li&gt;EMAAR,&lt;/li&gt;
&lt;li&gt;UNbank,&lt;/li&gt;
&lt;li&gt;Ebay,&lt;/li&gt;
&lt;li&gt;Alibaba group,&lt;/li&gt;
&lt;li&gt;CapitalOne&lt;/li&gt;
&lt;li&gt;Tencent,&lt;/li&gt;
&lt;li&gt;Square,&lt;/li&gt;
&lt;li&gt;SONOS... and many other organizations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Some applications around us which are built using Flutter...&lt;/h3&gt;

&lt;p&gt;For example, we have,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Ads at the top of this list,&lt;/li&gt;
&lt;li&gt;Insight Timer,&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.abbeyroad.com/" rel="noopener noreferrer"&gt;Abbey Road Studios,&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Baidu,&lt;/li&gt;
&lt;li&gt;ByteDance,&lt;/li&gt;
&lt;li&gt;Hamilton,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ahhh! I have just been talking about the positive aspects of Flutter. What about the drawbacks, Well Flutter has a handful number of disadvantages just as it has great abilities.&lt;/p&gt;

&lt;p&gt;So what are its drawbacks? Let's flutter together today to expose them. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr1se8eahtc6l3xpnlfu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr1se8eahtc6l3xpnlfu.gif" alt="Image description" width="346" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you were able to learn something from this article and wish this article will help you understand more and choose the right technology that will help you to build awesome apps either with or without Flutter. Take your time to choose and then get started right away.&lt;/p&gt;

&lt;p&gt;Want more? then you could check this &lt;a href="https://www.learn-dev-tools.blog/what-is-the-flutter-framework-a-beginners-introduction-to-flutter/" rel="noopener noreferrer"&gt;detailed article about Flutter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>opensource</category>
      <category>mobile</category>
      <category>beginners</category>
    </item>
    <item>
      <title>WordPress, Yet Another CMS</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Thu, 06 Apr 2023 14:17:06 +0000</pubDate>
      <link>https://dev.to/gunkev/wordpress-yet-another-cms-5bgh</link>
      <guid>https://dev.to/gunkev/wordpress-yet-another-cms-5bgh</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@markuswinkler?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Markus Winkler&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/wordpress?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently talked about &lt;a href="https://dev.to/gunkev/wordpresscom-vs-wordpressorg-which-one-should-you-use-4ho0"&gt;WordPress.com and WordPress.org&lt;/a&gt; in my previous articles and actually I have been getting requests from people asking what WordPress actually is. Today you will finally feel relief cause I will give you a brief introduction to WordPress. So guys sit and get a cup of tea to savour this.&lt;br&gt;
WordPress… just another CMS&lt;/p&gt;

&lt;p&gt;WordPress is actually a free and open source Content Management System written in PHP and associated with MySQL or MariaDB databases. Initially, it was created as a blog publishing-System. But it has evolved to include different web content types (such as online stores, membership websites, forums, and media galleries…).&lt;br&gt;
CMS… What is it?&lt;/p&gt;

&lt;p&gt;Content System Management also known as CMS is simply an application that is used to manage web content. In general, it allows a contributor or contributors to create, edit, publish or destroy content(web pages, articles…). A contributor can be any person who has access to a WordPress Dashboard&lt;/p&gt;

&lt;p&gt;WordPress is equally one of the most popular content system management used today. It has also been used for other application domains such as pervasive display systems.&lt;/p&gt;

&lt;p&gt;The software was released under the GPLv2 license. Founded by an American developer Matt Mullenweg and Mike Little an English developer. WordPress is also called a factory that makes webpages. It comes along with many tools to ease management. Some of these tools include themes and plugins.&lt;/p&gt;

&lt;p&gt;Below you will find a screenshot of a section of WordPress Dashboard&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8igv7y49ofzukd57v57.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8igv7y49ofzukd57v57.PNG" alt="Image description" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the shot above, the WordPress Dashboard has many tools you can use to manage your web content. To have a better experience just install WordPress and take a look around. If you would like to set up WordPress on your own don’t worry here is a tutorial on how to install it locally.&lt;/p&gt;

&lt;p&gt;To conclude, WordPress is an application used for building web platforms. Be it a blog, a website, or a portfolio, WordPress facilitates and eases your efforts to build such platforms. For example &lt;a href="https://www.tradnow.co" rel="noopener noreferrer"&gt;MangAnime tradnow&lt;/a&gt; and &lt;a href="https://www.learn-dev-tools.blog" rel="noopener noreferrer"&gt;Learndevtools&lt;/a&gt; are two blogs which talks about anime and development build essentially with WordPress&lt;/p&gt;

&lt;p&gt;Last but not least there exist different versions of WordPress. Some of us can use WordPress.com(Practically no coding). Then developers have the chance to get beautiful documentation in order to customize and bring in some innovations with WordPress.org.&lt;/p&gt;

&lt;p&gt;I hope I was able to answer most of your questions guys. Feel free to ask more questions. I will be glad to understand and help you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>wordpress</category>
      <category>cms</category>
    </item>
    <item>
      <title>Medusa Vs Woocommerce: Comparing Two Open source Online Commerce Platforms</title>
      <dc:creator>Gun</dc:creator>
      <pubDate>Mon, 20 Mar 2023 10:57:53 +0000</pubDate>
      <link>https://dev.to/gunkev/medusa-vs-woocommerce-comparing-two-open-source-online-commerce-platforms-4g8g</link>
      <guid>https://dev.to/gunkev/medusa-vs-woocommerce-comparing-two-open-source-online-commerce-platforms-4g8g</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://woocommerce.com" rel="noopener noreferrer"&gt;Woocommerce&lt;/a&gt; is an &lt;a href="https://www.learn-dev-tools.blog/what-does-open-source-software-mean-a-beginners-guide/" rel="noopener noreferrer"&gt;open source&lt;/a&gt;, customizable ecommerce platform built on WordPress. Woocommerce offers features like flexible and secure payment and shipping integration. It is written in PHP which is a universal language that can be used to build complex applications.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;a href="https://medusajs.com" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is an open source composable commerce engine built with Node.Js. It offers out-of-the-box features like multi-currency support, plugin integration support, and tax configurations among many others. Besides these, Medusa is known for its Return Merchandise Authorization (RMA) flows and how it is fully customizable.&lt;/p&gt;

&lt;p&gt;Through this article, you will learn why open source ecommerce tools are essential, how to choose a tool for your ecommerce, and the difference between Woocommerce and Medusa open source online commerce platforms per developer’s experience and the general ecommerce features they offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an Open Source Ecommerce Platform?
&lt;/h2&gt;

&lt;p&gt;Open source refers to software that has its source code publicly accessible for anyone to see, modify, modify, contribute to, and distribute as they want. An open source ecommerce platform is software that gives you full access to the source code, allowing you to customize the ecommerce platform to meet your needs and equally giving you total control over your store.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Woocommerce?
&lt;/h2&gt;

&lt;p&gt;Woocommerce is a &lt;a href="https://wordpress.org/plugins/woocommerce/" rel="noopener noreferrer"&gt;WordPress plugin&lt;/a&gt; used to build an online store. Woocommerce's first version was launched in 2011 and became extremely popular. In 2014, the plugin hit 4 million downloads.&lt;/p&gt;

&lt;p&gt;At the time of writing this article, Woocommerce has over 5 million active installations and 8.3k stars on GitHub. Woocommerce is a beginner-friendly tool, so you don’t need to be an expert developer to use it. Additionally, there are thousands of free and paid themes on Woocommerce.&lt;/p&gt;

&lt;p&gt;Woocommerce's main aim is to enable you to create an online store with just a few clicks and start selling. Woocommerce runs under WordPress; after creating your Woocommerce account, you will create a &lt;a href="https://wordpress.com/" rel="noopener noreferrer"&gt;WordPress account&lt;/a&gt; and give some permission to Woocommerce before building a store. You will need to set up a WordPress installation before setting up a Woocommerce store. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Medusa?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/Medusa-Vs-Woocommerce-Comparing-Two-Open-source-Online-Commerce-Platforms-d9ed9f2db73c4e6f9d336d7820cd7dcf" rel="noopener noreferrer"&gt;Medusa&lt;/a&gt; is a JavaScript-based open source tool for building online stores. Medusa was launched in 2021, aiming to provide a great experience to developers building an ecommerce platform, using NodeJs as an engine.&lt;/p&gt;

&lt;p&gt;Although Medusa is newer than Woocommerce,&amp;nbsp; it becomes increasingly popular each day as it’s being adopted by companies and individuals across the world. What attracts businesses and developers mostly is the customization ability Medusa offers.&lt;/p&gt;

&lt;p&gt;Medusa also offers other out-of-the-box features like bulk export and import, creation and management of multiple sales channels, configuration and management of many regions on one platform, addition, customization, and sorting of products into collections, and many more.&lt;/p&gt;

&lt;p&gt;Medusa  is made up of 3 components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://docs.medusajs.com/quickstart/quick-start/" rel="noopener noreferrer"&gt;Medusa Server&lt;/a&gt;: It’s a headless backend built on Node.js and it's the main component that contains all the logic and data of the store.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://docs.medusajs.com/admin/quickstart" rel="noopener noreferrer"&gt;Admin Dashboard&lt;/a&gt;: The store operator uses this component to manage the store, i.e. add, remove and update products, and customize and manage the store settings.&lt;/li&gt;
&lt;li&gt;The Storefront: Here, customers can view products and place orders. By default, Medusa offers 2 storefronts. One is built with &lt;a href="https://docs.medusajs.com/starters/nextjs-medusa-starter" rel="noopener noreferrer"&gt;NextJs&lt;/a&gt; and the other &lt;a href="https://docs.medusajs.com/starters/gatsby-medusa-starter" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt;, but you could build your own storefront using the &lt;a href="https://docs.medusajs.com/api/store/" rel="noopener noreferrer"&gt;Storefront REST APIs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Medusa Vs Woocommerce
&lt;/h2&gt;

&lt;p&gt;This section highlights the difference between Medusa and Woocommerce in terms of the general features both offer and the developer’s experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  General Features Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Payment Gateways
&lt;/h3&gt;

&lt;p&gt;Woocomerce and Medusa allow both developers and businesses to integrate third-party payment solutions. Woocommerce has a variety of ready-made extensions to integrate payment. Most of these payment gateways support Stripe, Paypal, Verifone, Apple Pay, and Amazon Pay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgobg96etcc8bhh9xdce3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgobg96etcc8bhh9xdce3.PNG" alt="payment woo commerce" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developers can integrate payment methods in their Medusa store using payment providers such as &lt;a href="https://docs.medusajs.com/add-plugins/klarna" rel="noopener noreferrer"&gt;Klarna&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/add-plugins/paypal" rel="noopener noreferrer"&gt;Paypal&lt;/a&gt;, and &lt;a href="https://docs.medusajs.com/add-plugins/stripe" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; with existing plugins. However, as a developer, you could also create a plugin to integrate other payment methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FpKPLMQjg9M9RmGi2fPxoK9ADavJmEaHmtZl6Wt5v2Wxn0BxgCSbnIL3kc6ClETJFzXC_9Zlg0teNpTh6y6RoXNU3cNClVTftGa7ihF8JAFTmqsvQ_XlJeWt0YV6tzebjxuJ85vdTseW-0bwBo6FHjlc-yjQjzoTKdSMPsN31Q3_vPmP6qd-6p31TxvHL8w" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FpKPLMQjg9M9RmGi2fPxoK9ADavJmEaHmtZl6Wt5v2Wxn0BxgCSbnIL3kc6ClETJFzXC_9Zlg0teNpTh6y6RoXNU3cNClVTftGa7ihF8JAFTmqsvQ_XlJeWt0YV6tzebjxuJ85vdTseW-0bwBo6FHjlc-yjQjzoTKdSMPsN31Q3_vPmP6qd-6p31TxvHL8w" alt="https://lh6.googleusercontent.com/pKPLMQjg9M9RmGi2fPxoK9ADavJmEaHmtZl6Wt5v2Wxn0BxgCSbnIL3kc6ClETJFzXC_9Zlg0teNpTh6y6RoXNU3cNClVTftGa7ihF8JAFTmqsvQ_XlJeWt0YV6tzebjxuJ85vdTseW-0bwBo6FHjlc-yjQjzoTKdSMPsN31Q3_vPmP6qd-6p31TxvHL8w" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Live-Chat Integrations
&lt;/h3&gt;

&lt;p&gt;A live chat will help engage more visitors and increase customer satisfaction across many channels.&lt;/p&gt;

&lt;p&gt;Woocommerce offers extensions like &lt;a href="https://woocommerce.com/products/livechat/?quid=cb0d6d6a56c15ac947f1f062efb6b885" rel="noopener noreferrer"&gt;LiveChat&lt;/a&gt; that you can install and set up on your store.&amp;nbsp; WordPress has many plugins for live chat Integration compatible with Woocommerce and some of them include LiveChat's WordPress Plugin, OLark WordPress Plugin, Tidio WordPress Plugin, &lt;a href="https://www.chatwoot.com/docs/product/channels/live-chat/integrations/wordpress" rel="noopener noreferrer"&gt;Chatwoot&lt;/a&gt;, and WP Social Chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FrPzqsSVziC9TrKXUmHppJINR-BPjSc8tN-XuBU5LxJS6eqsf-y1G7MQpxt8K0psyfPXH5V2HJAiE34K-S9yX-cftxqK-LyShdOEnueHHEzA81Ej5_I5w0uzl8-B6I_-reuJINqh_L06sbgCAl1RwZffNXgc0hJSSOSCva1qu40h8P2mb8TcJP14gCczhVg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FrPzqsSVziC9TrKXUmHppJINR-BPjSc8tN-XuBU5LxJS6eqsf-y1G7MQpxt8K0psyfPXH5V2HJAiE34K-S9yX-cftxqK-LyShdOEnueHHEzA81Ej5_I5w0uzl8-B6I_-reuJINqh_L06sbgCAl1RwZffNXgc0hJSSOSCva1qu40h8P2mb8TcJP14gCczhVg" alt="https://lh5.googleusercontent.com/rPzqsSVziC9TrKXUmHppJINR-BPjSc8tN-XuBU5LxJS6eqsf-y1G7MQpxt8K0psyfPXH5V2HJAiE34K-S9yX-cftxqK-LyShdOEnueHHEzA81Ej5_I5w0uzl8-B6I_-reuJINqh_L06sbgCAl1RwZffNXgc0hJSSOSCva1qu40h8P2mb8TcJP14gCczhVg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Medusa, on the other hand, offers many ways to &lt;a href="https://medusajs.com/blog/how-to-integrate-live-chat-with-gatsby-tidio-medusa/" rel="noopener noreferrer"&gt;integrate Live Chat with Tidio&lt;/a&gt;, Chatwoot, Zendesk, Hotspot, ChatBot, and many others. In addition, Medusa plugin systems make it easier to &lt;a href="https://docs.medusajs.com/advanced/backend/plugins/create" rel="noopener noreferrer"&gt;create plugins&lt;/a&gt; and integrate with these chat services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taxes
&lt;/h3&gt;

&lt;p&gt;Woocommerce offers a simple way to manage taxes, but before configuring your tax, you need to enable taxes. You can choose to either include taxes in your product’s prices or charge taxes separately. You can set up taxes to be applied in a specific region.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FvDv9CJBdJfdWri5_mozcdsbmZPueeuQQlbURyv1bLig6FxqVA1Tj5ntpWqLUC9nlP3PCsd465UFL8CrXRsfjfE6P78lfFu6wX25Z75jEByeeLKJdt9UiSlnDwQPu-WZ4ShJLD9mQjo50mXD5W8VmPt5K4HWoy6CLcJNBqDPGVsal72fq-YzLDs6w81vPJA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FvDv9CJBdJfdWri5_mozcdsbmZPueeuQQlbURyv1bLig6FxqVA1Tj5ntpWqLUC9nlP3PCsd465UFL8CrXRsfjfE6P78lfFu6wX25Z75jEByeeLKJdt9UiSlnDwQPu-WZ4ShJLD9mQjo50mXD5W8VmPt5K4HWoy6CLcJNBqDPGVsal72fq-YzLDs6w81vPJA" alt="https://lh6.googleusercontent.com/vDv9CJBdJfdWri5_mozcdsbmZPueeuQQlbURyv1bLig6FxqVA1Tj5ntpWqLUC9nlP3PCsd465UFL8CrXRsfjfE6P78lfFu6wX25Z75jEByeeLKJdt9UiSlnDwQPu-WZ4ShJLD9mQjo50mXD5W8VmPt5K4HWoy6CLcJNBqDPGVsal72fq-YzLDs6w81vPJA" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fqchh2VuEGFGUhFK-d5TWyKrxVPmyBTvpKwvGCoasahr7d0ppNcBmfHK4YODPw7EzTxlcQfY8bVWhNCOB20X9LqdqQxaIk-lHflQvyZIUEimE6TLBmagE9s6apAm1crePpHD7cmA4qgH_Vu2vfVA0guWvC2nzlfSihvPTdHHKmo8t3FSD9Y2HRlr8-82kbw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fqchh2VuEGFGUhFK-d5TWyKrxVPmyBTvpKwvGCoasahr7d0ppNcBmfHK4YODPw7EzTxlcQfY8bVWhNCOB20X9LqdqQxaIk-lHflQvyZIUEimE6TLBmagE9s6apAm1crePpHD7cmA4qgH_Vu2vfVA0guWvC2nzlfSihvPTdHHKmo8t3FSD9Y2HRlr8-82kbw" alt="https://lh4.googleusercontent.com/qchh2VuEGFGUhFK-d5TWyKrxVPmyBTvpKwvGCoasahr7d0ppNcBmfHK4YODPw7EzTxlcQfY8bVWhNCOB20X9LqdqQxaIk-lHflQvyZIUEimE6TLBmagE9s6apAm1crePpHD7cmA4qgH_Vu2vfVA0guWvC2nzlfSihvPTdHHKmo8t3FSD9Y2HRlr8-82kbw" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.medusajs.com/user-guide/taxes/" rel="noopener noreferrer"&gt;Taxes&lt;/a&gt; are available out-of-the-box in Medusa. Like Woocommerce, you can manage taxes in specific regions as well as add multiple tax rates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fap3ycJfK_XD7Cy25t_2omrSR9TQEG5bYzDekDZVoacSApuQ-LCbpV4F_vwIIhd9_YB2kqdnSX_lggYlJHbflXpZc4WnJ0tx9Vz_tnpQ7iASVjmGXgDJVOFOWDXIWzVF4thu3qth3T6-QHUQjsXQtNs-c-M_6JhtyAzya8q5DOpnwbhsi3rDAW6YJ8vo0QQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2Fap3ycJfK_XD7Cy25t_2omrSR9TQEG5bYzDekDZVoacSApuQ-LCbpV4F_vwIIhd9_YB2kqdnSX_lggYlJHbflXpZc4WnJ0tx9Vz_tnpQ7iASVjmGXgDJVOFOWDXIWzVF4thu3qth3T6-QHUQjsXQtNs-c-M_6JhtyAzya8q5DOpnwbhsi3rDAW6YJ8vo0QQ" alt="https://lh4.googleusercontent.com/ap3ycJfK_XD7Cy25t_2omrSR9TQEG5bYzDekDZVoacSApuQ-LCbpV4F_vwIIhd9_YB2kqdnSX_lggYlJHbflXpZc4WnJ0tx9Vz_tnpQ7iASVjmGXgDJVOFOWDXIWzVF4thu3qth3T6-QHUQjsXQtNs-c-M_6JhtyAzya8q5DOpnwbhsi3rDAW6YJ8vo0QQ" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Medusa provides a &lt;a href="https://docs.medusajs.com/api/admin/#tag/Tax-Rate" rel="noopener noreferrer"&gt;Tax API&lt;/a&gt; to developers that allow them to further integrate third-party services.&lt;/p&gt;

&lt;h3&gt;
  
  
  RMA Flows
&lt;/h3&gt;

&lt;p&gt;Woocommerce offers a powerful and reliable RMA tool for selling, managing, processing warranties, and handling return requests within your Woocommerce store. However, you need to download the &lt;a href="https://woocommerce.com/document/warranty-and-returns/" rel="noopener noreferrer"&gt;Woocommerce Product RMA&lt;/a&gt;, install, activate and configure it. Unfortunately, this product is not free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.medusajs.com/user-guide/orders/" rel="noopener noreferrer"&gt;RMA Flows&lt;/a&gt; are available out-of-the-box in Medusa and customers can send requests to return items from an ecommerce store. The admin or store operator can manage the order status, shipping, and payment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shipping
&lt;/h3&gt;

&lt;p&gt;Woocommerce allows you to set up shipping zones and add methods to those zones and rates to your shipping methods. Users can create as many zones as possible and add methods and rates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FyfE5MhBemMrsdApJY1LAnChSx_0EOYWrMo5-QMRE5hksyLSAzxSqFO9gRnWqgOwXIz1MVTYRIBbTyhetmvT2kc6HswxUkMzfDiipQcNK6OFPTYXO1nZ3nMSBl837Mih7P3paKdh_WiIBdhI68YLGRMQcrfVithx5OjNCNWIw36q5e71_cf5adHkX4u9XAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FyfE5MhBemMrsdApJY1LAnChSx_0EOYWrMo5-QMRE5hksyLSAzxSqFO9gRnWqgOwXIz1MVTYRIBbTyhetmvT2kc6HswxUkMzfDiipQcNK6OFPTYXO1nZ3nMSBl837Mih7P3paKdh_WiIBdhI68YLGRMQcrfVithx5OjNCNWIw36q5e71_cf5adHkX4u9XAA" alt="https://lh5.googleusercontent.com/yfE5MhBemMrsdApJY1LAnChSx_0EOYWrMo5-QMRE5hksyLSAzxSqFO9gRnWqgOwXIz1MVTYRIBbTyhetmvT2kc6HswxUkMzfDiipQcNK6OFPTYXO1nZ3nMSBl837Mih7P3paKdh_WiIBdhI68YLGRMQcrfVithx5OjNCNWIw36q5e71_cf5adHkX4u9XAA" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Medusa allows developers to integrate third-party providers like WebShipping. &lt;a href="https://docs.medusajs.com/user-guide/regions/shipping-options/" rel="noopener noreferrer"&gt;Medusa has 4 components in its shipping architecture&lt;/a&gt;: fulfillment provider*&lt;em&gt;,&lt;/em&gt;* &amp;nbsp;&lt;a href="https://docs.medusajs.com/user-guide/regions/shipping-options/" rel="noopener noreferrer"&gt;shipping options&lt;/a&gt;, shipping methods, and shipping profiles which are the highest in the hierarchy of shipping. However, developers can equally create plugins to manage any shipping provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FFovmJ3Wg-kquTFWru2h22QVSVUKPNwq40q_dxzIIpd5gX9J1UIcQTfpZNBAI1FB_J6zVjWXl_lsnfvgkvZrCiSByOPxb5-pL0xc244102QL4c9k_zSQMNTRA_b0I78eNm-NZuvHM92qjsuMxt46hXiHWWQNOicNgZx-a10gXM6tP__dF8pN3cVsCzu8Wkw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FFovmJ3Wg-kquTFWru2h22QVSVUKPNwq40q_dxzIIpd5gX9J1UIcQTfpZNBAI1FB_J6zVjWXl_lsnfvgkvZrCiSByOPxb5-pL0xc244102QL4c9k_zSQMNTRA_b0I78eNm-NZuvHM92qjsuMxt46hXiHWWQNOicNgZx-a10gXM6tP__dF8pN3cVsCzu8Wkw" alt="https://lh4.googleusercontent.com/FovmJ3Wg-kquTFWru2h22QVSVUKPNwq40q_dxzIIpd5gX9J1UIcQTfpZNBAI1FB_J6zVjWXl_lsnfvgkvZrCiSByOPxb5-pL0xc244102QL4c9k_zSQMNTRA_b0I78eNm-NZuvHM92qjsuMxt46hXiHWWQNOicNgZx-a10gXM6tP__dF8pN3cVsCzu8Wkw" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Admin Dashboard
&lt;/h3&gt;

&lt;p&gt;Woocommerce does not have an admin dashboard on its own but rather uses the WordPress dashboard since it’s functionally built to use WordPress. When you install the plugin, the Woocommerce menu appears on the sidebar of the WordPress dashboard. &lt;/p&gt;

&lt;p&gt;The dashboard is simple and allows you to manage products, orders, Extensions, Reports, and other features. You need to know how the WP admin dashboard works to use Woocommerce.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FYrJ0A8wgvxPas5rBXD535RQTvy9iAZOeJHfAdjxZheZnmDVeU1FP1vrMG42D0eoRbYDlHqw8CakAprg55__K50vmT4H1xE6l-YVVkEbcS0sB7c6q6_twIaWs42ysJDIBqvLNYQmVwKgc2raLBUfmLUvE0UPYOz7Cfxw6b7Ga330hQZ0pc_KTuT06-ulfGw" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FYrJ0A8wgvxPas5rBXD535RQTvy9iAZOeJHfAdjxZheZnmDVeU1FP1vrMG42D0eoRbYDlHqw8CakAprg55__K50vmT4H1xE6l-YVVkEbcS0sB7c6q6_twIaWs42ysJDIBqvLNYQmVwKgc2raLBUfmLUvE0UPYOz7Cfxw6b7Ga330hQZ0pc_KTuT06-ulfGw" alt="https://lh4.googleusercontent.com/YrJ0A8wgvxPas5rBXD535RQTvy9iAZOeJHfAdjxZheZnmDVeU1FP1vrMG42D0eoRbYDlHqw8CakAprg55__K50vmT4H1xE6l-YVVkEbcS0sB7c6q6_twIaWs42ysJDIBqvLNYQmVwKgc2raLBUfmLUvE0UPYOz7Cfxw6b7Ga330hQZ0pc_KTuT06-ulfGw" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Medusa has a simple and fluent &lt;a href="https://docs.medusajs.com/admin/quickstart" rel="noopener noreferrer"&gt;admin dashboard&lt;/a&gt;. The dashboard allows you to manage products, price listings, customers, regions, currencies, settings, and many other elements of your store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FonBj5fpd9Zg6QrWOGCmWXgid97HxXCCjtOdG88_bF0r77CRmhV8kMDo1CROHqL9inOKuYjFYoptBA9G0X7lB0wXzzxerpJNhkQFWCWafCrM0-bRutZnervJypH56sGDR6Frxu3t5x1pHgeZOIceLe3mO4x6xQdA6If93E1GA3_e75f7JgXZJaV1vm_sG0A" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FonBj5fpd9Zg6QrWOGCmWXgid97HxXCCjtOdG88_bF0r77CRmhV8kMDo1CROHqL9inOKuYjFYoptBA9G0X7lB0wXzzxerpJNhkQFWCWafCrM0-bRutZnervJypH56sGDR6Frxu3t5x1pHgeZOIceLe3mO4x6xQdA6If93E1GA3_e75f7JgXZJaV1vm_sG0A" alt="https://lh6.googleusercontent.com/onBj5fpd9Zg6QrWOGCmWXgid97HxXCCjtOdG88_bF0r77CRmhV8kMDo1CROHqL9inOKuYjFYoptBA9G0X7lB0wXzzxerpJNhkQFWCWafCrM0-bRutZnervJypH56sGDR6Frxu3t5x1pHgeZOIceLe3mO4x6xQdA6If93E1GA3_e75f7JgXZJaV1vm_sG0A" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Localization
&lt;/h3&gt;

&lt;p&gt;Woocommerce does not offer localization options on its own. To localize your store, you need to install a plugin like PoEdit, &lt;a href="https://woocommerce.com/document/woocommerce-localization/" rel="noopener noreferrer"&gt;Loco Translate&lt;/a&gt;, or &lt;a href="https://multilingualpress.org/" rel="noopener noreferrer"&gt;MultilingualPress&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Currently, the localization feature is not also available out-of-the-box in Medusa. But it can be implemented using CMS plugins like &lt;a href="https://docs.medusajs.com/add-plugins/contentful/" rel="noopener noreferrer"&gt;Contentful&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-currency Support
&lt;/h3&gt;

&lt;p&gt;Unfortunately, multi-currency is not available out–of–the–box in Woocommerce. To set up multiple currencies in your online store, you need to purchase the &lt;a href="https://woocommerce.com/products/currency-converter-widget/" rel="noopener noreferrer"&gt;Currency Converter Widget&lt;/a&gt; extension and set it up. There are other extensions like Woocommerce Multi-Currency or Multi-Currency Switcher which are also not free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FvEKitssP-Eck-6Wtn4UtvB67Aoj5X8AP1fjyjXM2rhnFx53zP2QSsqA-MHRRxGMNRHZqsNB4rNelWd9DYB7illMfNz_Sp_zgApveUs-_ttx8dxeW379umTjO8rH6_A71ETdHSLueK61q8UDsvhivg8KtO_uQIp_dX3zapl48Z3_kQ6-BS1UdUYA9M92ggg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FvEKitssP-Eck-6Wtn4UtvB67Aoj5X8AP1fjyjXM2rhnFx53zP2QSsqA-MHRRxGMNRHZqsNB4rNelWd9DYB7illMfNz_Sp_zgApveUs-_ttx8dxeW379umTjO8rH6_A71ETdHSLueK61q8UDsvhivg8KtO_uQIp_dX3zapl48Z3_kQ6-BS1UdUYA9M92ggg" alt="https://lh4.googleusercontent.com/vEKitssP-Eck-6Wtn4UtvB67Aoj5X8AP1fjyjXM2rhnFx53zP2QSsqA-MHRRxGMNRHZqsNB4rNelWd9DYB7illMfNz_Sp_zgApveUs-_ttx8dxeW379umTjO8rH6_A71ETdHSLueK61q8UDsvhivg8KtO_uQIp_dX3zapl48Z3_kQ6-BS1UdUYA9M92ggg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Medusa supports the multi-currency feature. A business can set up a region and choose specific regional settings such as currency. Additionally, the business can manage each region from one store, hence eliminating the need to create many stores or switch between them&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FVE8PaS7hQTQcP8I12mrA_-KAbDtOKmZ2GCWBb0AAx4bJ8TzLTvZKDReTm4NUatNRkNRNP6w_yWjrQAT8qmxQxJdfRaOCq-KcqI4ak8xZE8_uwJht01RTbVbfDVDhZdEqjY3cYZFAHVX1a10DMT8ECJMr-96KowCMwxIoJ-hk51680v7AAiy_C9popHOn6w" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh4.googleusercontent.com%2FVE8PaS7hQTQcP8I12mrA_-KAbDtOKmZ2GCWBb0AAx4bJ8TzLTvZKDReTm4NUatNRkNRNP6w_yWjrQAT8qmxQxJdfRaOCq-KcqI4ak8xZE8_uwJht01RTbVbfDVDhZdEqjY3cYZFAHVX1a10DMT8ECJMr-96KowCMwxIoJ-hk51680v7AAiy_C9popHOn6w" alt="https://lh4.googleusercontent.com/VE8PaS7hQTQcP8I12mrA_-KAbDtOKmZ2GCWBb0AAx4bJ8TzLTvZKDReTm4NUatNRkNRNP6w_yWjrQAT8qmxQxJdfRaOCq-KcqI4ak8xZE8_uwJht01RTbVbfDVDhZdEqjY3cYZFAHVX1a10DMT8ECJMr-96KowCMwxIoJ-hk51680v7AAiy_C9popHOn6w" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FqEDbcmsmsqtGhBIyQz89rbbJpLcm3rF4bLxHUgabCIJqdPYAcxCdlqg9FwSfKkr6WLM0GFEUYC89-zd0VBG2dtsTFkqKbQ3FUD6YFZ31nCs-6_x_YmxuJ6Sp15VKTabool-iGqBiCflxQ3_J2k1WzsVEeChCXN74XZ79xYUsMeGJPg0UhsK-nnruUBLYeA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh5.googleusercontent.com%2FqEDbcmsmsqtGhBIyQz89rbbJpLcm3rF4bLxHUgabCIJqdPYAcxCdlqg9FwSfKkr6WLM0GFEUYC89-zd0VBG2dtsTFkqKbQ3FUD6YFZ31nCs-6_x_YmxuJ6Sp15VKTabool-iGqBiCflxQ3_J2k1WzsVEeChCXN74XZ79xYUsMeGJPg0UhsK-nnruUBLYeA" alt="https://lh5.googleusercontent.com/qEDbcmsmsqtGhBIyQz89rbbJpLcm3rF4bLxHUgabCIJqdPYAcxCdlqg9FwSfKkr6WLM0GFEUYC89-zd0VBG2dtsTFkqKbQ3FUD6YFZ31nCs-6_x_YmxuJ6Sp15VKTabool-iGqBiCflxQ3_J2k1WzsVEeChCXN74XZ79xYUsMeGJPg0UhsK-nnruUBLYeA" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gift Cards
&lt;/h3&gt;

&lt;p&gt;You can create and sell prepaid multipurpose gift cards to customers, which they can redeem at your store. Gift cards are not available out-of-the-box in Woocommerce. Some Woocommerce gift card extensions require at least version 3.9. of Woocommerce. They are not free of charge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FvlB-FYpA6UMqBEOjSMTMoxvSFyJySL7W0eP_eWBoiFhZIiGwxNtOca0HJF5F-957fmfhvWC90YgNstj4g5MQcq9m0lzZyVZBCyhpQHoojoyqMt4vIrNc3N8CcEmj1EC7LGuZKz4v86hck9f_bUU3rsshLRqZaoBF9F0-E6fXG7ZuSHGTZbVh21Mhkbxqeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2FvlB-FYpA6UMqBEOjSMTMoxvSFyJySL7W0eP_eWBoiFhZIiGwxNtOca0HJF5F-957fmfhvWC90YgNstj4g5MQcq9m0lzZyVZBCyhpQHoojoyqMt4vIrNc3N8CcEmj1EC7LGuZKz4v86hck9f_bUU3rsshLRqZaoBF9F0-E6fXG7ZuSHGTZbVh21Mhkbxqeg" alt="https://lh3.googleusercontent.com/vlB-FYpA6UMqBEOjSMTMoxvSFyJySL7W0eP_eWBoiFhZIiGwxNtOca0HJF5F-957fmfhvWC90YgNstj4g5MQcq9m0lzZyVZBCyhpQHoojoyqMt4vIrNc3N8CcEmj1EC7LGuZKz4v86hck9f_bUU3rsshLRqZaoBF9F0-E6fXG7ZuSHGTZbVh21Mhkbxqeg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gift cards are available in Medusa out-of-the-box. Merchants can specify multiple denominations and images. Customers can then purchase the gift cards as if they were buying a product. However, gift cards are not packaged and shipped like products. Customers can receive it in the form of a link or by email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FhC2g4pu_hoWSDzs3rPHRTamRbjxtixH1MtPwONRKVDJg1f3HwUDEBE9NJgKJW0OnuR12g6BcUs8oORR2eLffRZHi9ZC1H93gVm0yIEPKNXwjB5yYBAXo3r31cJcirIRiFZTDDIOp9ayxmO9nngKLfPbP86d5pWZuGUgA7AePzbW3hbxNtm7SiJBBhdo4fA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FhC2g4pu_hoWSDzs3rPHRTamRbjxtixH1MtPwONRKVDJg1f3HwUDEBE9NJgKJW0OnuR12g6BcUs8oORR2eLffRZHi9ZC1H93gVm0yIEPKNXwjB5yYBAXo3r31cJcirIRiFZTDDIOp9ayxmO9nngKLfPbP86d5pWZuGUgA7AePzbW3hbxNtm7SiJBBhdo4fA" alt="https://lh6.googleusercontent.com/hC2g4pu_hoWSDzs3rPHRTamRbjxtixH1MtPwONRKVDJg1f3HwUDEBE9NJgKJW0OnuR12g6BcUs8oORR2eLffRZHi9ZC1H93gVm0yIEPKNXwjB5yYBAXo3r31cJcirIRiFZTDDIOp9ayxmO9nngKLfPbP86d5pWZuGUgA7AePzbW3hbxNtm7SiJBBhdo4fA" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reporting and Analytics
&lt;/h3&gt;

&lt;p&gt;Woocommerce offers an analytical board where you can view your number of sales, net sales, number of orders, products sold as well as variation sold. You can compare your sales per date and even download your reports for further utilization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FZhTL19GZWc2gq5djJoRBIxRf3r_FtIT1kWVjpNQCyjuLGorkBtfQfTJtELjADRavRImxQkmMALTCsssP0gIHLDjdQOaVx04tON2TC-thlSo-sLuIo2dx1n-wWZAUPtw1OQVhSW5qJ7u7G8ff-X1pxl7baUa4docpRXA-u748JSzI8QSUIJRtKz08upHDJQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh6.googleusercontent.com%2FZhTL19GZWc2gq5djJoRBIxRf3r_FtIT1kWVjpNQCyjuLGorkBtfQfTJtELjADRavRImxQkmMALTCsssP0gIHLDjdQOaVx04tON2TC-thlSo-sLuIo2dx1n-wWZAUPtw1OQVhSW5qJ7u7G8ff-X1pxl7baUa4docpRXA-u748JSzI8QSUIJRtKz08upHDJQ" alt="https://lh6.googleusercontent.com/ZhTL19GZWc2gq5djJoRBIxRf3r_FtIT1kWVjpNQCyjuLGorkBtfQfTJtELjADRavRImxQkmMALTCsssP0gIHLDjdQOaVx04tON2TC-thlSo-sLuIo2dx1n-wWZAUPtw1OQVhSW5qJ7u7G8ff-X1pxl7baUa4docpRXA-u748JSzI8QSUIJRtKz08upHDJQ" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right now, Medusa does not provide any analytic module but developers can use third-party services to add analytical features. Fortunately, with the flexibility Medusa offers, developers can easily implement and &lt;a href="https://docs.medusajs.com/advanced/backend/plugins/overview/" rel="noopener noreferrer"&gt;integrate third-party services&lt;/a&gt; in their store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed and Performance
&lt;/h3&gt;

&lt;p&gt;The process of creating a store with Woocommerce is quite fast. With a few clicks, you will have your store set up and ready to sell. If you need to measure the speed and performance in terms of the tools used to build the frontend and backend, this will be different since Woocommerce is not a headless commerce. You can optimize the speed and performance of your store in various ways.&lt;/p&gt;

&lt;p&gt;For example, if you decide to use a lighter framework or just pure CSS rather than using Bootstrap, the number of stylesheet files will considerably reduce, and the pages will load rapidly increasing user experience as well as some &lt;a href="https://www.learn-dev-tools.blog/topics/search-engine-optimization/" rel="noopener noreferrer"&gt;SEO&lt;/a&gt; factors hence improving the speed ad performance of your store&lt;/p&gt;

&lt;p&gt;Medusa's headless architecture allows you to separate the backend from the frontend. This makes it faster and lighter compared to tightly coupled architecture. So you can decide to use a lighter framework or fewer resources for both your backend and frontend and this can greatly impact the speed and performance of your store. &lt;/p&gt;

&lt;p&gt;In addition, you can set up your store following this &lt;a href="https://docs.medusajs.com/quickstart/quick-start" rel="noopener noreferrer"&gt;quick guide&lt;/a&gt; and the process is fast since in just three steps, you will now have a complete commerce engine running. However, if you need advanced functionalities then you can check the &lt;a href="https://docs.medusajs.com/" rel="noopener noreferrer"&gt;complete Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer’s Features Comparison
&lt;/h3&gt;

&lt;p&gt;This section presents a brief comparison between Medusa and Woocommerce in terms of developer features&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Both Woocommerce and Medusa provide extensive &lt;a href="https://www.learn-dev-tools.blog/topics/writing-good-documentation/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for both developers and business owners. &lt;a href="https://woocommerce.com/documentation/plugins/woocommerce/woocommerce-codex/" rel="noopener noreferrer"&gt;Woocommerce codex&lt;/a&gt; provides a library of documentation and tutorials to set up, customize and expand your online store whereas Medusa has a &lt;a href="https://docs.medusajs.com/user-guide/" rel="noopener noreferrer"&gt;detailed user guide&lt;/a&gt; to help you get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Community
&lt;/h3&gt;

&lt;p&gt;Developers can contribute or report bugs on &lt;a href="https://github.com/woocommerce/woocommerce" rel="noopener noreferrer"&gt;Woocommerce’s GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Medusa is an open source platform whose aim is to build a strong and collaborative relationship with the community. Both developers or tier persons can join the &lt;a href="https://discord.gg/medusajs" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; to stay up to date with current trends and request and receive help from the community members as well as the core Medusa team.&lt;/p&gt;

&lt;p&gt;Developers can equally showcase their work, report bugs, propose fixes or contribute to issues to the Medusa platform on its &lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. There are other channels like &lt;a href="https://twitter.com/medusajs" rel="noopener noreferrer"&gt;Twitter and LinkedIn&lt;/a&gt; that developers can join to bring their contribution or get help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation and Time to Get Started
&lt;/h3&gt;

&lt;p&gt;WooCommerce’s &lt;a href="https://woocommerce.com/documentation/plugins/woocommerce/getting-started/installation-and-updating/" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt; shows you a step-by-step process on how to install and set up your online store easily. To get started with Woocommerce, developers need to know PHP, HTML, CSS3, and WordPress. Non-developers can install WooCommerce via &lt;a href="http://wordpress.com/" rel="noopener noreferrer"&gt;WordPress.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Medusa provides a &lt;a href="https://docs.medusajs.com/quickstart/quick-start" rel="noopener noreferrer"&gt;quick guide&lt;/a&gt; that allows developers to create and manage their store in just 3 steps: Installing Medusa CLI with yarn or npm, creating a new Medusa project using the CLI, and starting the Medusa server.&lt;/p&gt;

&lt;p&gt;In less than 4 steps, you will have a store set up with the features mentioned earlier. You’ll then need to set up the &lt;a href="https://docs.medusajs.com/admin/quickstart" rel="noopener noreferrer"&gt;admin&lt;/a&gt; and storefront with Gatsby or NextJs to effectively manage your store. Medusa ecommerce store is written in Typescript and JavaScript essentially and uses SQLite as a database if no database engine is set up.&lt;/p&gt;

&lt;p&gt;For production purposes, it’s recommended to install and configure a &lt;a href="https://docs.medusajs.com/tutorial/set-up-your-development-environment#postgresql" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; database and &lt;a href="https://docs.medusajs.com/tutorial/set-up-your-development-environment#redis" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; to handle events. The step-by-step process to install and manage these tools for various OS is included in the &lt;a href="https://docs.medusajs.com/tutorial/set-up-your-development-environment/#nodejs" rel="noopener noreferrer"&gt;Medusa documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment and Upgrade
&lt;/h3&gt;

&lt;p&gt;Since Woocommerce is built on top of WordPress, it’s recommended to deploy the online store on a WordPress deployment plan hence restricting deployment on any cloud hosting. &lt;/p&gt;

&lt;p&gt;Woocommerce deals mostly with updates rather than upgrades. Before thinking of updating your Woocommerce store, it’s recommended to manually back up or &lt;a href="https://jetpack.com/upgrade/backup/" rel="noopener noreferrer"&gt;automatically back up&lt;/a&gt; your store. &lt;/p&gt;

&lt;p&gt;Unlike Woocommerce, Medusa can be deployed on any cloud hosting. In its documentation, you can find a straightforward guide on how to deploy on various platforms like &lt;a href="https://docs.medusajs.com/deployments/server/deploying-on-heroku" rel="noopener noreferrer"&gt;Heruko&lt;/a&gt;, &lt;a href="https://docs.medusajs.com/deployments/server/deploying-on-digital-ocean" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt;, or &lt;a href="https://docs.medusajs.com/deployments/server/deploying-on-qovery" rel="noopener noreferrer"&gt;Qovery&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Upgrading Medusa is quite simple; however, some versions may require you to run migration scripts or take additional actions. Fortunately, Medusa provides an &lt;a href="https://docs.medusajs.com/advanced/backend/upgrade-guides/" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt; with detailed steps to follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customization
&lt;/h3&gt;

&lt;p&gt;Woocommerce is not a headless architecture. Nevertheless, you can build a WooCommerce headless store on your own. This is not recommended, though, as Woocommerce is not designed for this purpose and it does not have the tools or features to build one.&lt;/p&gt;

&lt;p&gt;The fact that Medusa has a headless architecture allows anyone to easily and freely customize their storefront. You can choose any framework or programming language to use on your frontend. This also applies to the admin dashboard. Creating custom features is like adding a JavaScript file that loads automatically into Medusa as soon as you run the server.&lt;/p&gt;

&lt;p&gt;The backend &lt;a href="https://docs.medusajs.com/api/admin/" rel="noopener noreferrer"&gt;uses an API&lt;/a&gt; so developers can extend these APIs to add third-party services and custom features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer’s Level
&lt;/h3&gt;

&lt;p&gt;You don’t need to be a developer to be able to set up a store with WordPress and Woocommerce. You can set up your online store with just a few clicks. However, if you need advanced features and more customization, then you may need to hire an experienced developer to build those features or make them by yourself.&lt;/p&gt;

&lt;p&gt;Medusa has a simple and understandable architecture. As a developer, you just need knowledge of JavaScript/TypeScript and NodeJs. Both the Medusa storefront and admin dashboard are built with JavaScript Frameworks. &lt;/p&gt;

&lt;p&gt;Due to the decoupled nature of Medusa, you can create the admin dashboard and storefront with any language or framework. All you need is to link the backend using &lt;a href="https://docs.medusajs.com/api/store/auth" rel="noopener noreferrer"&gt;REST API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of Medusa vs Woocommerce
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Medusa&lt;/th&gt;
&lt;th&gt;Woocommerce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;REST API&lt;/td&gt;
&lt;td&gt;REST API but you need to enable it in the Woocommerce settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars on GitHub&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/medusajs/medusa" rel="noopener noreferrer"&gt;https://github.com/medusajs/medusa&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;8.3k&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/medusajs/medusa/blob/master/LICENSE" rel="noopener noreferrer"&gt;https://github.com/medusajs/medusa/blob/master/LICENSE&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activity&lt;/td&gt;
&lt;td&gt;Since 2021&lt;/td&gt;
&lt;td&gt;Since 2011&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latest Release&lt;/td&gt;
&lt;td&gt;1.7.5(5 days ago)&lt;/td&gt;
&lt;td&gt;7.3.0(2 weeks ago)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;WordPress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile App&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMA Flows&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taxes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Localization&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Region Support&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gift Cards&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headless Architecture&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customization&lt;/td&gt;
&lt;td&gt;Highly Customizable&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you learned the difference between Woocommerce and Medusa based on the features each ecommerce store offers and the developer’s experience. Choosing the best ecommerce platform is an important decision to reflect on because taking the wrong decision may greatly affect your business in the long run.&lt;/p&gt;

&lt;p&gt;Woocommerce is easy to set up and can handle both smaller and bigger businesses. It is a good choice for non-developers and developers who know PHP and WordPress.&lt;/p&gt;

&lt;p&gt;Medusa is a perfect choice for anyone aiming to build headless commerce. Medusa can equally manage both larger and smaller businesses. It’s a good choice for developers with great JS/TS skills, looking forward to building a NodeJs ecommerce Platform. If you want to design your storefront and admin dashboard then Medusa is also a good choice.&lt;/p&gt;

</description>
      <category>headless</category>
      <category>opensource</category>
      <category>commerce</category>
      <category>medusajs</category>
    </item>
  </channel>
</rss>
