<?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: Soo</title>
    <description>The latest articles on DEV Community by Soo (@soo_c622b93b5cd).</description>
    <link>https://dev.to/soo_c622b93b5cd</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%2F3965785%2Ffb5f68e6-c3ed-4f62-bd7d-3b176de2669e.png</url>
      <title>DEV Community: Soo</title>
      <link>https://dev.to/soo_c622b93b5cd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/soo_c622b93b5cd"/>
    <language>en</language>
    <item>
      <title>What I learned building my first side project: doing everything client-side</title>
      <dc:creator>Soo</dc:creator>
      <pubDate>Sun, 14 Jun 2026 12:55:13 +0000</pubDate>
      <link>https://dev.to/soo_c622b93b5cd/what-i-learned-building-my-first-side-project-doing-everything-client-side-opo</link>
      <guid>https://dev.to/soo_c622b93b5cd/what-i-learned-building-my-first-side-project-doing-everything-client-side-opo</guid>
      <description>&lt;p&gt;I built a website with &lt;strong&gt;no backend at all&lt;/strong&gt; — every tool runs in your browser, so your data never leaves your device.&lt;/p&gt;

&lt;p&gt;It's the first thing I've ever shipped, and honestly? I'm not sure I made the right call.&lt;/p&gt;

&lt;p&gt;I'd love for you to tell me. 👇&lt;/p&gt;




&lt;h2&gt;
  
  
  The annoyance that started it
&lt;/h2&gt;

&lt;p&gt;You know the loop.&lt;/p&gt;

&lt;p&gt;You need to format a chunk of JSON. Or decode a JWT to check what's inside. Or shrink an image before uploading it somewhere.&lt;/p&gt;

&lt;p&gt;So you Google it, click the first result, and land on a site that's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧱 buried in ads&lt;/li&gt;
&lt;li&gt;🔒 asking you to "sign up to continue"&lt;/li&gt;
&lt;li&gt;😬 telling you to &lt;strong&gt;"upload your file to our server"&lt;/strong&gt; — for something that obviously doesn't need one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one always bugged me. Why does compressing a PNG require &lt;em&gt;anyone's&lt;/em&gt; server?&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://utilify.run" rel="noopener noreferrer"&gt;&lt;strong&gt;Utilify&lt;/strong&gt;&lt;/a&gt;: the small tools I reach for daily, in one clean place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: nothing leaves your device
&lt;/h2&gt;

&lt;p&gt;I gave myself one hard constraint:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every tool runs 100% in the browser. No backend. Ever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Turns out the browser can already do almost all of it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;What does the work&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Format JSON&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JSON.parse&lt;/code&gt; / &lt;code&gt;JSON.stringify&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compress images&lt;/td&gt;
&lt;td&gt;the Canvas API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate IDs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;crypto.randomUUID()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base64&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;btoa&lt;/code&gt; / &lt;code&gt;atob&lt;/code&gt; + &lt;code&gt;TextEncoder&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Decoding a JWT payload? It's genuinely one line:&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;payload&lt;/span&gt; &lt;span class="o"&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&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="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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No request. No upload. Nothing for me to log, leak, or get hacked out of.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I didn't expect
&lt;/h2&gt;

&lt;p&gt;I went backend-less for privacy. But it quietly fixed three other things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Fast&lt;/strong&gt; — no server round-trip, so results are instant&lt;/li&gt;
&lt;li&gt;📶 &lt;strong&gt;Works offline&lt;/strong&gt; — once the page loads, you can pull the plug&lt;/li&gt;
&lt;li&gt;💸 &lt;strong&gt;Basically free to run&lt;/strong&gt; — which is the only reason it can stay free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One decision, four wins. That rarely happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  What building it actually taught me
&lt;/h2&gt;

&lt;p&gt;I couldn't fake my way through the topics — I had to understand them. A few things that surprised me enough to write up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👉 &lt;a href="https://utilify.run/blog/what-is-base64-encoding" rel="noopener noreferrer"&gt;Base64 is &lt;strong&gt;encoding, not encryption&lt;/strong&gt;&lt;/a&gt; — and people mix this up constantly&lt;/li&gt;
&lt;li&gt;👉 &lt;a href="https://utilify.run/blog/base64-vs-base64url" rel="noopener noreferrer"&gt;Base64 vs Base64URL&lt;/a&gt; — two characters (&lt;code&gt;+ /&lt;/code&gt; vs &lt;code&gt;- _&lt;/code&gt;) cause a &lt;em&gt;shocking&lt;/em&gt; amount of pain&lt;/li&gt;
&lt;li&gt;👉 &lt;a href="https://utilify.run/blog/how-to-decode-jwt-in-javascript" rel="noopener noreferrer"&gt;How to decode a JWT in JS&lt;/a&gt; — and why you should never trust an unverified one&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where I'm honestly still unsure
&lt;/h2&gt;

&lt;p&gt;This is my first shipped project, so I'd love a reality check from people who've done this more than once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is "no backend" a feature users actually &lt;em&gt;care&lt;/em&gt; about — or just a thing I find satisfying?&lt;/li&gt;
&lt;li&gt;Canvas image compression is fine, not amazing. Is WASM worth the complexity for a free tool?&lt;/li&gt;
&lt;li&gt;How do you know a tool is &lt;em&gt;done&lt;/em&gt; vs. just endlessly polishing?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it / roast it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://utilify.run/tools/json-formatter" rel="noopener noreferrer"&gt;JSON formatter&lt;/a&gt; and &lt;a href="https://utilify.run/tools/jwt-decoder" rel="noopener noreferrer"&gt;JWT decoder&lt;/a&gt; get the most use so far. Free, no signup.&lt;/p&gt;

&lt;p&gt;If you've shipped something like this, &lt;strong&gt;be blunt in the comments&lt;/strong&gt; — that's the whole reason I'm posting instead of sitting on it.&lt;/p&gt;

&lt;p&gt;And the question I keep coming back to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What tiny utility do you keep wishing existed in one clean, fast place?&lt;/strong&gt; That's how I pick what to build next. 👇&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
