DEV Community

Mark
Mark

Posted on

150+ Tools with Only 2 Layout Components: UtlKit Architecture Design

150+ Tools with Only 2 Layout Components: UtlKit Architecture Design

UtlKit Series Part 2/2: Architecture and Trade-offs
Part 1 covers the story and tech choices → Read Part 1


150+ tools, but only 2 core layouts. Good architecture doubles development speed.

Overall Architecture

Overall Architecture

Directory Structure

src/
├── app/
│   ├── layout.tsx          # Root layout: Sentry, fonts, theme
│   ├── page.tsx            # Homepage: category tabs + tool grid
│   ├── page.client.tsx     # Homepage client-side logic
│   ├── tools/
│   │   ├── json-formatter/
│   │   │   ├── page.tsx    # Page + SEO metadata
│   │   │   └── formatter.tsx # Tool core logic
│   │   ├── hash-generator/
│   │   ├── base64/
│   │   └── ... (150+ tools total)
│   ├── about/page.tsx
│   ├── contact/page.tsx
│   ├── privacy/page.tsx
│   └── terms/page.tsx
├── components/
│   ├── ConverterLayout.tsx  # Shared layout for converter tools
│   ├── ToolSection.tsx      # Shared layout for calculator tools
│   ├── Header.tsx           # Nav + search + language + theme toggle
│   ├── Footer.tsx
│   ├── Breadcrumbs.tsx
│   ├── ThemeProvider.tsx
│   └── AdSlot.tsx
├── lib/
│   ├── tools.ts             # Tool metadata (150+ tools: name/icon/category)
│   ├── i18n/
│   │   ├── en.ts            # English ~2200 lines
│   │   └── zh-CN.ts         # Chinese ~2200 lines
│   ├── md5.ts               # Pure JS MD5 implementation
│   ├── sentry-release.ts
│   └── tool-seo.ts
└── styles/
    └── globals.css          # Tailwind + CSS custom properties
Enter fullscreen mode Exit fullscreen mode

Component Abstraction: 80% of Tools Share Layouts

150+ tools distilled into two core layouts:

1. ConverterLayout — Input → Process → Output

JSON formatter, Base64, Hash, and similar tools all use this:

┌─────────────────────────────────┐
│  Input          │  Output       │
│  textarea       │  textarea     │
│                 │               │
│  [Convert]      │  [Copy]       │
└─────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

2. ToolSection — Form → Calculate → Result

BMI, Compound Interest, Loan calculator and similar:

┌────────────────────────┐
│  [Input 1]             │
│  [Input 2]             │
│  [Input 3]             │
│                        │
│  [Calculate]           │
│                        │
│  ─── Result ───        │
│  Result display area   │
└────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

These two shared layouts + minimal custom logic per tool dramatically improved development efficiency. Each tool only needs to implement its core conversion logic — the UI is handled uniformly by the layout components.

Browser-Only Trade-offs

The Biggest Trade-off: No Node.js Ecosystem

Node.js has mature formatting/minification libraries, but the browser doesn't have fs, net, or child_process.

Library Node.js Dependency Alternative
terser (JS minification) fs Custom regex minifier
html-minifier-terser fs Custom regex minifier
xml-formatter minify ⚠️ ESM/CJS issues Custom regex minifier
js-beautify (formatting) ❌ Browser-compatible ✅ Use directly
csso (CSS minification) ❌ Pure JS ✅ Use directly

Conclusion: Use existing libraries for formatting, write custom regex for minification.

Full analysis in Part 3 → Why Node.js Libraries Don't Work in the Browser

Encryption: Web Crypto API Limitations

The browser natively supports SHA-256/384/512, AES-GCM, and HMAC — but not MD5.

MD5 is proven insecure, but it's still widely used in practice (file checksums, legacy system compatibility, etc.).

Solution: Implement MD5 yourself (~100 lines of pure JS, bitwise operations).

Full analysis in Part 5 → Client-Side Encryption with Web Crypto API

i18n: Bilingual Design

src/lib/i18n/
├── en.ts       (2208 lines)
└── zh-CN.ts    (2207 lines)
Enter fullscreen mode Exit fullscreen mode

Usage:

const { t } = useI18n()
<button>{t('btn.copy')}</button>  // "Copy" / "复制"
Enter fullscreen mode Exit fullscreen mode

Key decisions:

  • Not using next-intl — SSR safety issues in static export mode
  • localStorage + Context — Simple and controllable
  • All page titles bilingual — SEO-friendly for both languages
  • Tool names also bilingual — Search, breadcrumbs, sitemap

From 0 to 1 to 100

These two articles cover the "0 to 1" — design decisions and architecture. The following articles cover the "1 to 100" — real-world pitfalls:

Part Title Core Problem
1 React 19 Hydration Mismatch in Static Export SSR/CSR inconsistency
2 Cloudflare Pages Blank Page: the index.txt Bug Content negotiation mechanism
3 Why Node.js Libraries Fail in the Browser fs dependencies, ESM/CJS interop
4 Sentry Source Maps: From 0% to Full Resolution Tree-shaking, HTTP API, region detection
5 Client-Side Encryption with Web Crypto API MD5 gap, AES-GCM, HMAC
6 Free Performance Monitoring: PageSpeed CI Zero-cost Core Web Vitals monitoring
7 Cloudflare Cache Hit Rate: 7% → 90% 3 Cache Rules + localization optimization
8 Perfect Dark Mode: From Hydration Error to Zero Flicker Troubleshooting 8 pitfalls
9 6 Pitfalls of Multilingual Sites query string → path prefix, 3 rounds of redesign

Project Info

  • Website: utlkit.com
  • Stack: Next.js 15 + React 18 + TypeScript + Tailwind CSS
  • Hosting: Cloudflare Pages
  • Tools: 150+ tools, 8 categories
  • Cost: $0/month

If you find this helpful, follow along for the rest of the series. Constructive feedback welcome in the comments.

Top comments (0)