<?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: SKUMart</title>
    <description>The latest articles on DEV Community by SKUMart (@skumart).</description>
    <link>https://dev.to/skumart</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3962513%2F4057d1d2-7fcf-4e70-be0d-576b4fe525cb.png</url>
      <title>DEV Community: SKUMart</title>
      <link>https://dev.to/skumart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skumart"/>
    <language>en</language>
    <item>
      <title>How to Convert Supplier CSVs to Shopify/Amazon/WooCommerce Without Uploading to a Server</title>
      <dc:creator>SKUMart</dc:creator>
      <pubDate>Mon, 01 Jun 2026 10:46:03 +0000</pubDate>
      <link>https://dev.to/skumart/how-to-convert-supplier-csvs-to-shopifyamazonwoocommerce-without-uploading-to-a-server-5a0f</link>
      <guid>https://dev.to/skumart/how-to-convert-supplier-csvs-to-shopifyamazonwoocommerce-without-uploading-to-a-server-5a0f</guid>
      <description>&lt;p&gt;Every e-commerce supplier sends files differently. One uses &lt;code&gt;REF&lt;/code&gt; and &lt;code&gt;DESIGNATION&lt;/code&gt;, another uses &lt;code&gt;SKU&lt;/code&gt; and &lt;code&gt;Description&lt;/code&gt;. Some ship XLSX with merged cells. Others hand you a UTF-16 CSV that Excel refuses to open.&lt;br&gt;
The traditional solution: upload to a server, wait for parsing, hit errors, fix the file, repeat. This wastes time and exposes sensitive pricing data to a third party.&lt;br&gt;
I built &lt;a href="https://skumart.com" rel="noopener noreferrer"&gt;SKUMart&lt;/a&gt; — a client-side CSV/XLSX converter that auto-detects columns in 6 languages and maps them to Shopify, Amazon, WooCommerce, Etsy, or eBay. Everything happens in the browser. No upload, no server, no signup.&lt;br&gt;
Here's how it works under the hood.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Client-Side
&lt;/h2&gt;

&lt;p&gt;The decision to process everything in the browser wasn't just about cost savings (zero server bills). It's a privacy feature: supplier inventory data, wholesale prices, and margin calculations never leave the user's machine.&lt;br&gt;
Technically, the app uses &lt;a href="https://www.papaparse.com/" rel="noopener noreferrer"&gt;Papa Parse&lt;/a&gt; for CSV and &lt;a href="https://sheetjs.com/" rel="noopener noreferrer"&gt;SheetJS&lt;/a&gt; for XLSX, both loaded dynamically from CDN. The parsed rows are stored in a simple JavaScript array — no WebAssembly, no worker pool, just plain array operations that complete in under 50ms for typical supplier files.&lt;/p&gt;
&lt;h2&gt;
  
  
  Auto-Detection — The Hard Part
&lt;/h2&gt;

&lt;p&gt;The core challenge: a column header like &lt;code&gt;DESCRIPTION&lt;/code&gt; could map to &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;body_html&lt;/code&gt;, or &lt;code&gt;short_description&lt;/code&gt; depending on the target platform. A French supplier's &lt;code&gt;PRIX_ACHAT&lt;/code&gt; needs to become &lt;code&gt;price&lt;/code&gt;. A German &lt;code&gt;BEZEICHNUNG&lt;/code&gt; should map to &lt;code&gt;title&lt;/code&gt;.&lt;br&gt;
The detector works by building a keyword map for each target field across all 6 supported languages (EN, FR, DE, ES, IT, ZH). Each source header is scored against every target field using a simple weighting system:&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;scoreHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lang&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;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;targetField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;en&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;headerLower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;header&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;[^&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9&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="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&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;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;kw&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;keywords&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;headerLower&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&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;else&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;headerLower&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;kw&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&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;kw&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;headerLower&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;score&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;The highest-scoring pair wins. If no confident match is found, the column is flagged for manual mapping. This approach handles 90%+ of supplier files out of the box.&lt;br&gt;
The Mapping Engine&lt;br&gt;
Once columns are detected, users can override the auto-map or build custom mappings. The engine supports 5 e-commerce schemas out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify: 40+ fields (title, body_html, variants, images, metafields)&lt;/li&gt;
&lt;li&gt;Amazon Flat File: 30+ fields (SKU, ASIN, price, quantity, fulfillment)&lt;/li&gt;
&lt;li&gt;WooCommerce: 25+ fields via CSV import spec&lt;/li&gt;
&lt;li&gt;Etsy: 20+ fields (listing, inventory, shipping)&lt;/li&gt;
&lt;li&gt;eBay: 30+ fields (item specifics, variations, shipping)
Each schema is a JSON file defining field names, data types, and validation rules. The mapper walks source → target assignments and applies type coercion (string → number, date formatting, etc.) before export.
The entire app stays under 200 KB gzipped by avoiding heavy UI libraries. It's built with &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; + &lt;a href="https://github.com/developit/htm" rel="noopener noreferrer"&gt;htm&lt;/a&gt; (no JSX build step) and styled with &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS v4&lt;/a&gt;.
Export &amp;amp; Practical Considerations
Export uses the standard Blob + download API. No server round-trip — just an ephemeral &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; click with URL.createObjectURL(). The free tier handles 50 rows, enough to validate the mapping before committing.
Column mapping profiles can be saved and reused, which is useful when the same supplier sends weekly inventory files with identical headers.
Try It
If you deal with supplier files regularly, you might find this useful. &lt;a href="https://skumart.com" rel="noopener noreferrer"&gt;SKUMart&lt;/a&gt; handles all of this out of the box — just drop a file and export.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
