DEV Community

Cover image for I built 8 free maker tools: six never upload your files, two fetch a URL for you
RadarMakers
RadarMakers

Posted on

I built 8 free maker tools: six never upload your files, two fetch a URL for you

Launching a small product means a pile of small chores: a favicon set, a few screenshots in a nice frame, tracked links for every channel, a changelog, a launch checklist, and a check that your link previews actually work. We built tools for these into RadarMakers, a launch platform for indie makers, and made them free with no account.

There are eight of them, and they split into two honest groups. Six run entirely in your browser. Two need to fetch a public page, so they send the address you type to our server. This post explains both groups and the code pattern that keeps all eight easy to test.

All eight tools grouped into six browser tools and two server URL checkers

The six that stay in your browser

  • Launch UTM link builder. Builds tracked links for many channels and destination pages at once, imports a CSV, checks that campaign names are spelled consistently and downloads a campaign sheet.
  • Launch date planner. Turns a launch date into a dated task list you can tick off and export as a calendar file or Markdown.
  • Screenshot mockup and launch image exporter. Frames a real screenshot with padding, a background and a shadow, lets you blur private details, and exports launch images at several sizes.
  • Favicon and PWA icon package builder. Turns one logo into a favicon.ico, PNG sizes and a web manifest, with a real 16 pixel preview.
  • Batch image optimizer with a file size target. Compresses several screenshots towards a target size and gives you the files plus a CSV report.
  • Changelog and release notes generator. Turns a list of changes into a Markdown or HTML changelog, grouped by type.

Four of these accept files, and each reads them in the page: the UTM builder reads a CSV with FileReader.readAsText, the favicon builder loads the logo through an object URL into an Image, and the screenshot mockup and image optimizer read the file with file.arrayBuffer() and decode it with createImageBitmap. The results are drawn on a canvas or built in JavaScript and offered as downloads made from a Blob, or a ZIP for the favicon package. These six tools process your working input locally and create downloads in your browser. They do not upload your files.

The two that need a server

The meta tag checker and the Open Graph and social card checker answer a different question: "what does this live page actually tell search engines and social networks?" A browser cannot reliably read arbitrary sites unless those sites allow cross origin access. So these two tools send the address to our backend. The server fetches the page, follows permitted redirects, reads its tags and checks any referenced share images. It reads the HTML without running the page's JavaScript, so the previews approximate what each platform shows rather than reproduce it exactly.

Your browser sends the URL, our server fetches the page, follows redirects and checks share images, and your browser shows the report

We say this on the page, in plain words, above the input:

The address you type is sent to our server. The server downloads that public page once, reads its tags and sends the result back. Neither the address nor the result is stored, and the limit is 20 checks per minute.

That wording is a little shorter than reality, since the server also follows redirects and checks share images, as described above. On the server side, three decisions matter:

  • The handler and the inspector do not log or persist the submitted URL or the inspection result.
  • The fetch goes through a dialer that rejects loopback, private, link local and other blocked IP ranges on every connection.
  • A rate limiter allows 20 requests per minute per IP, shared by both checkers.
// cmd/server/main.go, excerpt
inspector := inspect.New(audit.NewPublicOnlyClient(8*time.Second, 0))
inspectLimiter := custommw.NewRateLimiter(20, time.Minute)
r.With(inspectLimiter.Middleware).Post("/tools/inspect-url", handler.NewToolsInspectHandler(inspector).Inspect)
Enter fullscreen mode Exit fullscreen mode

Both checkers also have a manual mode, where you type a title and description or pick an image from your disk to preview a card. That mode stays local like the other six tools.

The pattern that keeps them testable

We separate reusable processing logic from React components, with shared inspection code for both URL checkers. Most tools have a logic.ts with pure functions and no React or DOM, next to a component that handles input, canvas drawing and downloads. Each tool has its own test file that runs in plain Node with node --test. Today the eight tools plus their shared URL inspector have 199 test declarations between them, covering the logic, some component interactions, generated files and server rendered page HTML.

A small example from the UTM builder. Campaign names that differ only in case or spacing split your analytics into separate rows, so the tool normalizes them and tells you what it removed instead of silently transliterating:

// utm-link-builder/logic.ts
export function cleanValue(raw: string, normalize: boolean, separator: Separator): { value: string; removed: string[] } {
  const trimmed = raw.trim();
  if (!normalize) return { value: trimmed, removed: [] };
  const removed: string[] = [];
  let kept = "";
  for (const char of trimmed.toLowerCase()) {
    if (/[a-z0-9_\s-]/.test(char)) kept += char;
    else if (!removed.includes(char)) removed.push(char);
  }
  const value = kept.trim().replace(/\s+/g, separator);
  return { value, removed };
}
Enter fullscreen mode Exit fullscreen mode
// tests/utm-link-builder.test.mjs, excerpt
test("cleanValue lowercases, converts whitespace and reports skipped characters without transliteration", () => {
  assert.deepEqual(cleanValue("  Café Launch!! \t2026 ", true, "-"), { value: "caf-launch-2026", removed: ["é", "!"] });
});
Enter fullscreen mode Exit fullscreen mode

Because the logic does not touch the DOM, the same functions run in the browser and in the test runner. Canvas drawing is not covered by these Node tests. In the image optimizer, the encoder is passed into the logic as a callback, so the tests exercise our size targeting rather than the browser's codecs.

What I would do the same way again

  • Say where the data goes. "Runs in your browser" is a strong promise. When a tool cannot keep it, say so next to the input, not in a privacy policy.
  • Build downloads from Blobs. For images, CSV and text, the browser can do the whole job, so there is no reason to upload a file only to hand it back.
  • Keep logic out of components where you can. Pure functions are what make a refactor safe to test without clicking through the page.

The tools are free to use without an account. If you build tools like these, I would like to hear how you handle the line between local processing and server fetches.

Top comments (0)