<?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: Jack Green</title>
    <description>The latest articles on DEV Community by Jack Green (@jack_green_7b74cb2cdf9e23).</description>
    <link>https://dev.to/jack_green_7b74cb2cdf9e23</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%2F1995706%2F84828d29-3bb0-490e-b1b3-1733aad9a88b.jpg</url>
      <title>DEV Community: Jack Green</title>
      <link>https://dev.to/jack_green_7b74cb2cdf9e23</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jack_green_7b74cb2cdf9e23"/>
    <language>en</language>
    <item>
      <title>Building a 100% Client-Side PDF Invoice Generator with jsPDF (No Backend, No Sign-Up)</title>
      <dc:creator>Jack Green</dc:creator>
      <pubDate>Thu, 02 Jul 2026 03:28:02 +0000</pubDate>
      <link>https://dev.to/jack_green_7b74cb2cdf9e23/building-a-100-client-side-pdf-invoice-generator-with-jspdf-no-backend-no-sign-up-4lo1</link>
      <guid>https://dev.to/jack_green_7b74cb2cdf9e23/building-a-100-client-side-pdf-invoice-generator-with-jspdf-no-backend-no-sign-up-4lo1</guid>
      <description>&lt;p&gt;Freelancers send a handful of invoices a month, yet most invoicing tools charge a monthly subscription. I wanted the opposite: a tool you open in a browser, fill in, and get a professional PDF — no account, no server, no data leaving your machine.&lt;/p&gt;

&lt;p&gt;Here's how I built it, and the interesting problems along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint that shaped everything: no backend
&lt;/h2&gt;

&lt;p&gt;The entire app is static files: one HTML page, some CSS, and four small JS modules. PDF generation happens in the browser with &lt;a href="https://github.com/parallax/jsPDF" rel="noopener noreferrer"&gt;jsPDF&lt;/a&gt; plus the &lt;a href="https://github.com/simonbengtsson/jsPDF-AutoTable" rel="noopener noreferrer"&gt;autoTable plugin&lt;/a&gt; for line items.&lt;/p&gt;

&lt;p&gt;That constraint buys three things for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt; — client names and amounts never touch a server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline&lt;/strong&gt; — it works with WiFi off; useful on trains and planes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero hosting cost&lt;/strong&gt; — it's static, so it deploys anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Generating the PDF
&lt;/h2&gt;

&lt;p&gt;The core is surprisingly small. jsPDF gives you a canvas-like imperative API, and autoTable handles the line-item table with automatic pagination:&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;jspdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFontSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;doc&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVOICE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoTable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;startY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;head&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="s1"&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="s1"&gt;Qty&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;Rate&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;Amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="na"&gt;body&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desc&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;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;fmt&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;rate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;fmt&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;qty&lt;/span&gt; &lt;span class="o"&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;rate&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;striped&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`invoice-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.pdf`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The details that took actual time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Currency formatting&lt;/strong&gt; — &lt;code&gt;Intl.NumberFormat&lt;/code&gt; handles symbols and thousands separators correctly for every locale, so don't hand-roll it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long descriptions&lt;/strong&gt; — autoTable wraps cell text, but you need &lt;code&gt;columnStyles&lt;/code&gt; with fixed widths or totals drift off the page edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Totals section&lt;/strong&gt; — drawn manually after the table using &lt;code&gt;doc.lastAutoTable.finalY&lt;/code&gt; as the anchor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Templates with localStorage
&lt;/h2&gt;

&lt;p&gt;Freelancers invoice the same clients repeatedly, so the app saves templates as plain JSON in &lt;code&gt;localStorage&lt;/code&gt; — again, no server:&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="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice-template-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One catch: &lt;code&gt;localStorage&lt;/code&gt; is per-origin, so if you later move domains, saved templates don't follow. Export/import as a JSON file covers backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark mode without a toggle
&lt;/h2&gt;

&lt;p&gt;CSS custom properties plus &lt;code&gt;prefers-color-scheme&lt;/code&gt; means the app follows the OS setting with zero JS:&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="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--ink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#111827&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefers-color-scheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;--bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0f172a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="py"&gt;--ink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e2e8f0&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;The PDF itself always renders light — invoices get printed, and toner is expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The business experiment
&lt;/h2&gt;

&lt;p&gt;I'm selling this the boring way: a &lt;strong&gt;$9 one-time purchase&lt;/strong&gt; (via Creem, a merchant-of-record, so VAT is handled). The free demo generates 5 watermarked invoices; the paid version is the same app, unlocked, delivered as a ZIP you own forever. No subscription, because charging monthly for a tool that runs entirely on your computer feels wrong.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free demo (no email, nothing):&lt;/strong&gt; &lt;a href="https://invoice.jackgreen.top/app.html?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=invoice-launch" rel="noopener noreferrer"&gt;https://invoice.jackgreen.top/app.html?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=invoice-launch&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product page:&lt;/strong&gt; &lt;a href="https://invoice.jackgreen.top/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=invoice-launch" rel="noopener noreferrer"&gt;https://invoice.jackgreen.top/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=invoice-launch&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I'd genuinely like to hear what breaks or what's missing — comments here are perfect.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>I Built a CSV Data Cleaner That Saves Hours — Free Tool</title>
      <dc:creator>Jack Green</dc:creator>
      <pubDate>Wed, 24 Jun 2026 21:40:12 +0000</pubDate>
      <link>https://dev.to/jack_green_7b74cb2cdf9e23/i-built-a-csv-data-cleaner-that-saves-hours-free-tool-gb3</link>
      <guid>https://dev.to/jack_green_7b74cb2cdf9e23/i-built-a-csv-data-cleaner-that-saves-hours-free-tool-gb3</guid>
      <description>&lt;h1&gt;
  
  
  Why CSV Cleaning Matters
&lt;/h1&gt;

&lt;p&gt;Every data analyst and developer has faced this: messy spreadsheets with duplicates, wrong date formats, encoding issues, and trailing spaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: CSV Cleaner
&lt;/h2&gt;

&lt;p&gt;I built a free web tool that cleans CSV files in seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove duplicates&lt;/li&gt;
&lt;li&gt;Standardize dates&lt;/li&gt;
&lt;li&gt;Fix encoding&lt;/li&gt;
&lt;li&gt;Trim whitespace&lt;/li&gt;
&lt;li&gt;And 5 more operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="http://23.27.134.52:3000" rel="noopener noreferrer"&gt;http://23.27.134.52:3000&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Drag-drop your CSV file&lt;/li&gt;
&lt;li&gt;Select cleaning operations&lt;/li&gt;
&lt;li&gt;Download the clean file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All processing happens in your browser — no data leaves your computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the Pro Version
&lt;/h2&gt;

&lt;p&gt;Need CLI access and priority support? &lt;a href="https://wutaotao.gumroad.com/l/stripe-ui-kit" rel="noopener noreferrer"&gt;Get the CSV Cleaner Pro on Gumroad&lt;/a&gt; for just .&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
      <category>data</category>
    </item>
    <item>
      <title>How to Make Money as a Developer in 2026 — 4 Paths That Actually Pay</title>
      <dc:creator>Jack Green</dc:creator>
      <pubDate>Wed, 24 Jun 2026 10:05:24 +0000</pubDate>
      <link>https://dev.to/jack_green_7b74cb2cdf9e23/how-to-make-money-as-a-developer-in-2026-4-paths-that-actually-pay-ba9</link>
      <guid>https://dev.to/jack_green_7b74cb2cdf9e23/how-to-make-money-as-a-developer-in-2026-4-paths-that-actually-pay-ba9</guid>
      <description>&lt;h1&gt;
  
  
  How to Make Money as a Developer in 2026
&lt;/h1&gt;

&lt;p&gt;The old path — learn to code, get a job — is fading. Today, developers build multiple income streams.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Freelance Work
&lt;/h2&gt;

&lt;p&gt;Fiverr and Upwork let you find clients immediately. Specialize in Stripe integrations or API development and charge 0-150/hr.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Digital Products
&lt;/h2&gt;

&lt;p&gt;Sell UI kits and templates on Gumroad. I built a &lt;a href="https://wutaotao.gumroad.com/l/stripe-ui-kit" rel="noopener noreferrer"&gt;Stripe Dashboard UI Kit — 15 components, 9&lt;/a&gt;. Gumroad handles payment, delivery, and VAT.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Micro-SaaS
&lt;/h2&gt;

&lt;p&gt;Build a small tool, charge -50/month. Use my &lt;a href="http://23.27.134.52:3000" rel="noopener noreferrer"&gt;free Stripe Checkout Generator&lt;/a&gt; to add payments in 10 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Affiliate Revenue
&lt;/h2&gt;

&lt;p&gt;Write about tools you love. &lt;a href="https://www.digitalocean.com/referral" rel="noopener noreferrer"&gt;DigitalOcean gives 5 credit per referral&lt;/a&gt;. &lt;a href="https://www.siteground.com/affiliates" rel="noopener noreferrer"&gt;SiteGround pays 0-100&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Building
&lt;/h2&gt;

&lt;p&gt;Pick one path and ship this weekend. Need a dashboard UI? &lt;a href="https://wutaotao.gumroad.com/l/stripe-ui-kit" rel="noopener noreferrer"&gt;Grab the kit for 9&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
