<?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: RuBekOn</title>
    <description>The latest articles on DEV Community by RuBekOn (@rubekon_580e43fb0).</description>
    <link>https://dev.to/rubekon_580e43fb0</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%2F3883195%2F1d5778d3-792a-4d31-9244-78fae2885f80.jpg</url>
      <title>DEV Community: RuBekOn</title>
      <link>https://dev.to/rubekon_580e43fb0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rubekon_580e43fb0"/>
    <language>en</language>
    <item>
      <title>I built 44 free tools with no npm, no backend, no tracking</title>
      <dc:creator>RuBekOn</dc:creator>
      <pubDate>Thu, 16 Apr 2026 21:18:44 +0000</pubDate>
      <link>https://dev.to/rubekon_580e43fb0/i-built-44-free-tools-with-no-npm-no-backend-no-tracking-209m</link>
      <guid>https://dev.to/rubekon_580e43fb0/i-built-44-free-tools-with-no-npm-no-backend-no-tracking-209m</guid>
      <description>&lt;p&gt;Every time I needed a basic online tool - QR generator, PDF editor, image compressor - the first page of Google gave me three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Paywall after one use&lt;/li&gt;
&lt;li&gt;Upload your file to some sketchy server&lt;/li&gt;
&lt;li&gt;So many ads the tool barely worked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I built &lt;a href="https://atomnyx.com/" rel="noopener noreferrer"&gt;AtomnyX&lt;/a&gt; - 44 free browser tools (plus 34 side-by-side AI tool comparisons) that run 100% client-side. No signup, no tracking, no ads.&lt;/p&gt;

&lt;p&gt;The interesting part isn't what I built. It's the stack: &lt;strong&gt;no npm, no framework, no build step, no backend server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack (or lack of it)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Vanilla HTML, CSS, JavaScript. No React, no Vue, no bundler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; Netlify (free tier), deploys via git push&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend (for content):&lt;/strong&gt; Firebase Firestore - only for the blog and glossary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools:&lt;/strong&gt; 100% client-side using Canvas API, Web Crypto, File API, PDF.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing:&lt;/strong&gt; Netlify &lt;code&gt;_redirects&lt;/code&gt; for slug-based URLs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic stuff:&lt;/strong&gt; Netlify Edge Functions (Deno) for sitemap, RSS, article meta injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every tool page is just an HTML file with an inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no build step?
&lt;/h2&gt;

&lt;p&gt;Every time I've picked up a "simple" side project and chosen React + Vite + TailwindCSS, I lose two weekends to config before I write a single useful line.&lt;/p&gt;

&lt;p&gt;For a tools site, a build step is pure overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each tool is independent&lt;/li&gt;
&lt;li&gt;No shared state between pages&lt;/li&gt;
&lt;li&gt;The "app" is the tool itself&lt;/li&gt;
&lt;li&gt;SEO wants server-rendered (or pre-rendered) HTML anyway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When adding a new tool, the workflow is:&lt;br&gt;
Create /tools/new-tool.html&lt;br&gt;
Write the UI + logic inline&lt;br&gt;
git push&lt;br&gt;
Done (live in ~45 seconds)&lt;br&gt;
No &lt;code&gt;npm install&lt;/code&gt;. No webpack config. No "why is my dev server broken again."&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean URLs without a framework
&lt;/h2&gt;

&lt;p&gt;Netlify's &lt;code&gt;_redirects&lt;/code&gt; file handles all the routing magic. For tools, I use:&lt;br&gt;
Keep index accessible at /tools/&lt;br&gt;
/tools/index.html /tools/index.html 200&lt;/p&gt;

&lt;p&gt;301 old .html URLs to clean URLs (SEO safety)&lt;br&gt;
/tools/*.html /tools/:splat 301&lt;/p&gt;

&lt;p&gt;Serve clean URLs by rewriting to the .html file&lt;br&gt;
/tools/* /tools/:splat.html 200&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;/tools/qr-code-generator&lt;/code&gt; serves &lt;code&gt;/tools/qr-code-generator.html&lt;/code&gt; without the user ever seeing the extension. Old &lt;code&gt;.html&lt;/code&gt; URLs 301 to clean ones, so I don't lose any existing link equity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service worker for offline tools
&lt;/h2&gt;

&lt;p&gt;Most of my tools don't need the internet after the first load. That's a perfect fit for a service worker:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
javascript
// sw.js
const CACHE_NAME = 'atomnyx-v1';
const TOOLS_PAGES = [
  '/tools/',
  '/tools/qr-code-generator',
  '/tools/password-generator',
  '/tools/pdf-editor',
  // ...44 more
];
self.addEventListener('install', event =&amp;gt; {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache =&amp;gt; cache.addAll(TOOLS_PAGES))
  );
});
self.addEventListener('fetch', event =&amp;gt; {
  // Cache-first for tool pages (offline support)
  // Network-first for everything else
  if (event.request.url.includes('/tools/')) {
    event.respondWith(
      caches.match(event.request).then(res =&amp;gt; res || fetch(event.request))
    );
  }
});
After one visit, my QR generator works in airplane mode. Small win, huge UX difference.

Dynamic sitemap via Netlify Edge Function
I have a blog backed by Firestore. New articles get published constantly. Maintaining a static sitemap.xml by hand is a game I will always lose.

Enter Netlify Edge Functions (Deno runtime, runs at the edge, replaces the static file response on the fly):

// netlify/edge-functions/sitemap.js
export default async function handler(request, context) {
  // Fetch published articles from Firestore REST API
  const articles = await fetchFromFirestore();

  const urls = [
    ...STATIC_PAGES,
    ...articles.map(a =&amp;gt; ({
      url: `https://atomnyx.com/blog/${a.slug}`,
      lastmod: a.updatedAt,
      changefreq: 'weekly',
      priority: '0.9'
    }))
  ];

  const xml = `&amp;lt;?xml version="1.0"?&amp;gt;
    &amp;lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&amp;gt;
      ${urls.map(u =&amp;gt; `&amp;lt;url&amp;gt;&amp;lt;loc&amp;gt;${u.url}&amp;lt;/loc&amp;gt;&amp;lt;lastmod&amp;gt;${u.lastmod}&amp;lt;/lastmod&amp;gt;&amp;lt;/url&amp;gt;`).join('')}
    &amp;lt;/urlset&amp;gt;`;

  return new Response(xml, {
    headers: {
      'Content-Type': 'application/xml',
      'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400'
    }
  });
}
Configured in netlify.toml:

[[edge_functions]]
  path = "/sitemap.xml"
  function = "sitemap"
Every crawler that hits /sitemap.xml gets a fresh, live sitemap. No manual update. No build-time generation. 1-hour edge cache means Firestore barely notices.

A real debugging story: the QR generator
I wrote a QR code generator in 20 minutes using the qrcode-generator library. Worked in local testing. Deployed. User reported: "it's broken - says data too long for every URL."

Opened the live page, ran this in the console:

var ECC_MAP = { 'L': 1, 'M': 0, 'Q': 3, 'H': 2 };

// Test every combination
['numeric 0', 'numeric 1', 'string L', 'string M'].forEach(label =&amp;gt; {
  try {
    var qr = qrcode(4, /* ecc */);
    qr.addData('hello');
    qr.make();
    console.log(label, 'SUCCESS');
  } catch(e) {
    console.log(label, 'FAIL:', e);
  }
});
Result:

numeric 0: FAIL - bad rs block @ typeNumber:4/errorCorrectionLevel:undefined
numeric 1: FAIL - bad rs block @ typeNumber:4/errorCorrectionLevel:undefined
string L:  SUCCESS
string M:  SUCCESS
The library's docs said "error correction level: 0-3" — but the actual implementation only accepted string values ('L', 'M', 'Q', 'H'). My ECC_MAP was converting strings to numbers that the library couldn't parse.

The fix was one line:

- var eccLevel = ECC_MAP[eccSelect.value];
+ var eccLevel = eccSelect.value;  // pass string directly
Lesson: always test a library with the exact input types you'll be passing, not what the docs imply. Especially with older JS libraries where "enum" often means "string or number, who knows."

Auto-submitting to IndexNow on every deploy
I wrote a Python script that POSTs all my URLs to IndexNow (Bing + Yandex) so they crawl new content within minutes instead of weeks. Then I realized I'd always forget to run it.

Netlify Build Plugins to the rescue:

// netlify/plugins/indexnow-submit/index.js
module.exports = {
  onSuccess: async ({ utils }) =&amp;gt; {
    if (process.env.CONTEXT !== 'production') return;  // skip previews
    try {
      await utils.run.command('python3 indexnow-submit.py');
    } catch (err) {
      console.warn(`[IndexNow] Non-blocking failure: ${err.message}`);
    }
  }
};
Now every git push = automatic ping to search engines. Zero manual work.

What I'd do differently
1. JSON-LD structured data from day one.
I added it in month 4. Should've done it from day one. Rich results in Google = 2-3x click-through rate for the same ranking position.

2. Opinionated design system sooner.
I let CSS sprawl for too long. Eventually had to do a pass to replace all hardcoded hex colors with CSS custom properties for dark/light mode support. Should have started with CSS variables.

3. Analytics from day one.
I didn't track anything for the first month. Now I have no baseline to compare against. Set up GA4 + Plausible before you ship anything.

4. Service worker earlier.
I added it month 5. Tools work offline now, but if I'd had it from launch, all my early users would've had a much better first impression.

What's next
Auto-generated OG images per article (edge function rendering to SVG → PNG)
An n8n pipeline that generates article drafts from tech news RSS
More compare pages (I've written 34, targeting 100)
A Chrome extension wrapper for the top 3 tools
Maybe open-sourcing the whole thing
The site
If you want to poke at any of this:

Site: atomnyx.com
Tools: atomnyx.com/tools
AI tool comparisons: atomnyx.com/compare
Happy to answer questions about any of the technical choices in the comments. The "no npm, no framework" thing is either a fun constraint or heresy depending on who you ask - I'd love to hear both sides.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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