If you write documentation on GitHub, GitLab, or use Obsidian, you deal with Markdown tables. And you know they are a pain to write by hand.
The Hack
Aligning pipes (|), escaping special characters, and maintaining readability in plain text is tedious. The usual shortcut is to copy data from Excel or a CSV file, paste it into a free online converter, and grab the markdown output.
But here is the massive problem: where does that pasted data go?
Most of these tools process data on a backend server. If you are pasting internal company analytics, financial data, or API schemas, you are transmitting sensitive information to a third party. If your company requires you to sign an NDA, using these online converters is a direct violation of data privacy policies.
I needed a solution that was fast, easily accessible, and, most importantly, 100% local. Since I couldn’t find a tool that checked all the boxes without requiring a CLI environment, I built CSV to Markdown (.md) — a Chrome extension that does the conversion entirely in your browser.
The Tech Stack
I wanted this to be extremely fast and lightweight. No heavy frameworks.
- Vanilla JavaScript (Zero build steps, pure performance)
- Chrome Extensions API (Manifest V3)
- PapaParse (For robust CSV parsing)
- JSZip (For generating batch archives directly in memory)
Under the Hood (The Code)
The extension’s primary job is mapping parsed CSV data into a Markdown table structure.
Once the data is parsed from the clipboard or an uploaded file, the core generation logic builds the headers and rows. Here is a simplified version of how the Markdown is generated:
const generateMarkdownTable = (parsedData) => {
if (!parsedData || parsedData.length === 0) return '';
const headers = parsedData[0];
// Generate the separator row: |---|---|---|
const separator = headers.map(() => '---').join(' | ');
// Map rows and join them with pipes
const rows = parsedData.map(row => row.join(' | '));
return `${rows[0]}\n${separator}\n${rows.slice(1).join('\n')}`;
};
Enter fullscreen mode Exit fullscreen mode
For batch processing, instead of converting a single string, the extension loops through an array of files, processes them, and packs them into a .zip file using JSZip, triggering a download without ever touching a server.
Security & Permissions
Because this tool is designed for developers working under NDAs, security was the primary focus.
The extension operates with zero network requests. You can open the Network tab in your DevTools, paste your data, and click convert — nothing is sent out. The extension only requires minimal permissions necessary to read clipboard data when the user explicitly clicks the paste button. Your data never leaves your machine.
Challenges
1. Complex CSV Parsing
Initially, I thought I could just parse the CSV by splitting strings by commas and newlines: text.split('\n').map(row => row.split(',')).
That broke immediately. What if a cell contains a comma? What if it has line breaks or escaped quotes? Writing a custom regex parser was fragile, so I integrated PapaParse, which handles all edge cases gracefully right in the browser.
2. Client-Side Batch Downloads
One of the PRO features is batch processing — dropping 20 CSV files and getting them back as Markdown. The challenge was forcing the browser to download a generated .zip file without a backend. I used JSZip to generate a Blob in memory, created a temporary <a> tag, set its href to a URL.createObjectURL(blob), and triggered a programmatic click to start the download.
Try it out & Contribute
The extension is available in the Chrome Web Store. The free version allows up to 5 conversions per day, which covers most daily documentation tasks.
If you need unlimited batch processing (drag-and-drop multiple files) or smart column alignment, I offer a Lifetime PRO version via Gumroad.
If you find a bug, want to request a feature, or just want to explore the code, check out the repository. Contributions and issues are welcome!
Stop writing tables by hand and stop leaking your company’s data to random websites. Keep it local.
답글 남기기