DEV Community

Michael Schroeder
Michael Schroeder

Posted on

I built 57 browser-based dev tools in a month — here's how the architecture works

About a month ago, I launched UtilYard, a free collection of developer tools, text utilities, finance calculators, and image tools all in one place:

https://utilyard.com

I had a simple goal for the site. Create a clean, fast site where people could find useful tools without bouncing between a dozen different websites, release it, and basically just see where it goes. While I do have more ideas for the site, I mostly have a "let's see where this goes" mentality towards the site

Building the tools was not the hard part. The hard part was making the site easy to scale with ever expanding tools. I did not want to have to rewrite the pages for the every tool, so I developed a routing for each tool into a standard layout. Every tool lives under /tools/[slug], and a single page.tsx handles all of them.

The foundation is a central routes.json file that describes each tool:

{
  "path": "/tools/base64",
  "name": "Base64 Encoder / Decoder",
  "category": "developer",
  "tags": ["base64", "encode", "decode"],
  "faqNamespace": "Base64Tool"
}
Enter fullscreen mode Exit fullscreen mode

During the build process, generateStaticParams() reads that registry and generates a static page for every tool. No request-time rendering, just static HTML that's fast to load and easy for search engines to crawl.

export async function generateStaticParams() {
  return toolRoutes.flatMap(t =>
    routing.locales.map(locale => ({
      locale,
      slug: t.path.replace('/tools/', '')
    }))
  )
}
Enter fullscreen mode Exit fullscreen mode

Rendering the tools

Each tool is its own React component, and a simple registry maps slugs to components:

const registry: Record<string, ComponentType> = {
  'base64': Base64Tool,
  'lorem-ipsum-generator': LoremIpsumGenerator,
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Adding a new tool is simple. You build the component, add it to the register, then add the entry to the routes.json. Everything needed for the tool) metadata, breadcrumbs, related tools, structured data) gets generated automatically.

The FAQ schema is generated from translation files. If a tool has FAQ entries in its translation namespace, they're automatically converted into JSON-LD.

for (let i = 1; i <= 8; i++) {
  try {
    const q = tf(`q${i}` as any)
    const a = tf(`a${i}` as any)
    if (q && a) faqs.push({ q, a })
  } catch {
    break
  }
}
Enter fullscreen mode Exit fullscreen mode

Early results for the site has been the following. Its been indexing for about three weeks now, and search impressions have been growing week over week. It's still early, but it's encouraging to see organic traffic starting to pick up. Still looking to expand the site further. It be frank, it's been fun building this site so I want to develop it even more and see where it goes.

Happy to answer questions about the architecture, SEO setup, or anything else related to the build.

https://utilyard.com

Top comments (0)