How I Optimized Performance Building the Snappy-Fix Platform
Quick Tips Exclusive

How I Optimized Performance Building the Snappy-Fix Platform

Snappy‑Fix TeamMarch 20, 20268 min read
Share

When I started building Snappy-Fix — both the online tools platform and the admin dashboard — I had one clear goal in mind: every interaction must feel instant. No spinners where they can be avoided. No layout shifts. No sluggish tables. No freezing editors. In the world of online tools and content management, speed is not a feature — it is the product itself.

This article is the honest breakdown of how I approached performance across the entire Snappy-Fix stack: Next.js on the frontend, FastAPI on the tools backend, and a Go-powered API serving the admin dashboard. The mistakes I made, the strategies that worked, and the architectural decisions that transformed a slow prototype into a platform that feels genuinely snappy — by name and by experience.

The Reality Check: What Was Breaking Early

Before solutions, the honest picture of what the initial builds looked like.

Frontend Problems

  • UI freezing during file processing operations

  • The TipTap editor is causing hydration mismatches and 500 errors on first load

  • The admin blog table is loading the entire News[] array client-side — all records, all at once

  • Layout shift on image-heavy pages is destroying Core Web Vitals scores

  • Marketing site assets bleeding into the admin bundle — global navigation, tracking scripts, heavy footers all loading for authenticated admin users

Backend Problems

  • File uploads load entire files into server memory before processing begins

  • Blocking synchronous processing on the FastAPI tools backend is causing request queues

  • No cleanup of temporary files — disk space accumulating silently

  • API response times on the Go backend inconsistent under concurrent admin load

The root cause of almost every problem was the same: doing too much, too early, for too many. Loading all data when only a slice was needed. Rendering on the server what only the client could know. Sending the entire bundle when only one route was loaded. The fixes, once the pattern was clear, followed naturally.


Pillar One: Structural Efficiency — Decoupling the Admin From Everything Else

The single architectural decision with the highest performance return in the Next.js frontend was route grouping.

Without route groups, the admin dashboard shared its layout with the marketing site. Every admin page load triggered parsing of the global navigation, the marketing footer, the homepage hero fonts, and every tracking script attached to the public site. None of these has any business being in memory while an admin is editing a blog post.

The fix:

app/
├── (marketing)/          ← Marketing layout — fonts, nav, footer, analytics
│   ├── page.tsx
│   ├── blog/
│   └── tools/
├── (admin)/              ← Admin layout — sidebar, topbar only. Zero marketing overhead
│   ├── layout.tsx        ← AdminShell lives here exclusively
│   ├── blog/
│   └── categories/

What Route Isolation Achieved

Before Route Groups

After Route Groups

Admin loads marketing fonts

Admin loads only admin fonts

Global analytics scripts on every admin page

Analytics only on public routes

Full navigation re-renders between admin pages

Sidebar and topbar persist, only content re-renders

Shared bundle — 340KB+ JS on admin routes

Isolated bundle — 180KB on admin routes

Navigating between "Blog Posts" and "Categories" in the admin no longer triggers a full re-render of the shell. The AdminShell — sidebar, topbar, breadcrumb — is mounted once and persists. Only the route content slot updates. This is Next.js App Router's nested layout system working exactly as designed, but only if you deliberately isolate your layout groups.


Pillar Two: Solving the TipTap Hydration Bottleneck

The TipTap rich text editor is the most complex component in the Snappy-Fix admin. It is also the one that caused the most performance and stability issues before it was handled correctly.

The initial implementation mounted TipTap directly in a Server Component context. The result: hydration mismatches, 500 errors on first load, and the main thread locking up during the initial paint as the browser tried to reconcile the server-rendered editor shell with the client-side editor reality.

The Three-Part TipTap Fix

1. immediatelyRender: false The most impactful single-line fix. Setting immediatelyRender: false in the useEditor hook tells TipTap not to attempt a server-side render at all. The shell loads instantly. The editor mounts cleanly once the client environment is available.

2. Conditional client-only mounting

const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
if (!mounted) return <EditorSkeleton />
return <TipTapEditor />

The skeleton placeholder eliminates layout shift during mount. The editor appears to load instantly because something is always visible — the skeleton during SSR, the real editor immediately after hydration.

3. Dynamic import with ssr: false

const RichEditor = dynamic(
  () => import('@/components/editor/RichEditor'),
  { ssr: false, loading: () => <EditorSkeleton /> }
)

This removes the editor's JavaScript entirely from the server-rendered HTML. The initial page load is a fraction of the size. The editor JS downloads in the background and hydrates silently.


Pillar Three: Server-Side Pagination — Killing the Table Bloat

Admin panels are notorious for what I call Table Bloat — fetching every record from the database on page load and filtering, sorting, and paginating client-side. It works at 50 records. It fails at 500. It crashes at 5,000.

The Snappy-Fix blog listing initially fetched the entire News[] array. As the content library grew, load times grew proportionally.

The migration to server-side pagination was the highest-impact backend change in the admin dashboard.

The Pagination Architecture

// Every admin list query follows this contract
const params: Record<string, any> = {
  page: currentPage,
  limit: limit,           // 10 | 20 | 50 — user controlled
}
if (search)         params.search = search
if (category_id)    params.category_id = category_id
if (is_featured)    params.is_featured = true
if (is_exclusive)   params.is_exclusive = true

The backend returns a pagination tuple [PaginationMeta, totalCount] on every response. The frontend renders only the current page slice — never the full dataset.

Impact of Server-Side Pagination

Metric

Client-Side Pagination

Server-Side Pagination

Initial data transfer

All records (~2–8MB)

One page (~20–80KB)

Time to first render

3–6 seconds

Under 500ms

Memory usage

Grows with content volume

Constant

Search performance

Client JS filtering

Database index search

Works on 10,000 records


Pillar Four: File Handling and Backend Processing

The tools platform — image converters, PDF processors, optimisers — presented the most technically demanding performance challenges. Files are large, processing is CPU-intensive, and users expect results in seconds.

The File Handling Overhaul

Streaming uploads instead of memory loading: The initial implementation loaded the entire uploaded file into server memory before processing began. For a 10MB image, this meant 10MB of RAM allocated per concurrent user, multiplied by every user uploading simultaneously.

Streaming processes the file as bytes arrive — memory usage stays constant regardless of file size.

Async background processing: Heavy operations moved off the request thread entirely:

Operation

Before

After

Image compression

Blocking — the user waits

Async — immediate acknowledgement

PDF generation

Blocking — timeout risk

Background task — progress polling

Format conversion

Synchronous

Non-blocking async handler

File cleanup

Never — disk accumulation

Auto-cleanup after delivery

File size limits and early validation: Client-side validation rejects invalid file types and oversized files before they reach the server. This eliminates a whole category of server round-trips — the fastest API call is the one that never happens.


Pillar Five: Asset Management and CDN Delivery

With a content platform handling blog thumbnails, gallery images, and tool output files, unmanaged assets are a silent performance killer.

The Asset Strategy

  • Cloudinary for all media — automatic WebP conversion, responsive srcset generation, and CDN edge delivery from nodes closest to the user

  • next/image everywhere — automatic lazy loading, explicit dimensions preventing CLS, and priority flag on above-the-fold images only

  • Low-fidelity placeholders in the admin update view — the blog thumbnail preview loads a blurred placeholder while the full image fetches

  • Drag-and-drop client validation — file type and size validated in the browser before upload begins, preventing wasted server requests

Image Optimisation Impact

Metric

Unoptimised

Optimised

Average image weight

800KB–2MB

80–200KB

Format

JPEG/PNG

WebP/AVIF

Layout shift (CLS)

0.18 — failing

0.04 — passing

LCP on blog pages

4.2s

1.6s


Pillar Six: Memory Management and Reliability Over Time

Performance is not just about speed — it is about reliability over time. A dashboard that starts fast and degrades over a long editing session is not performant.

Two patterns prevented memory accumulation in the Snappy-Fix admin:

Effect cleanup on every subscription:

useEffect(() => {
  const handler = () => setSidebarOpen(false)
  window.addEventListener('resize', handler)
  return () => window.removeEventListener('resize', handler)
}, [])

Every addEventListener In the mobile sidebar, every resize observer, every interval-based polling function has a corresponding cleanup in the useEffect return. Without this, every component mount adds a listener and nothing removes it — memory grows until the tab is refreshed.

beforeunload warning on unsaved editor changes: The blog editor warns users before navigating away with unsaved content. Beyond the UX benefit, the beforeunload listener itself is cleaned up correctly on component unmount, preventing the ghost-listener accumulation that causes subtle memory leaks in long-running sessions.


Before vs After: The Full Platform Picture

Metric

Before Optimisation

After Optimisation

Admin initial bundle

~340KB JS

~180KB JS

Blog list load time

3–6 seconds

Under 500ms

TipTap editor mount

Hydration errors

Clean, instant

Tools page load

4–6 seconds

Under 2 seconds

Image weight (average)

800KB+

Under 200KB

CLS score

0.18 (failing)

0.04 (passing)

LCP score

4.2 seconds

1.6 seconds

Memory after 1hr session

Growing

Stable


The Lessons That Cost the Most to Learn

  1. Never do on the client what the server can do before the response arrives

  2. Never load all records when you will display ten

  3. Never mount a complex editor without isolating it from SSR

  4. Route groups are not a cosmetic choice — they are a performance boundary

  5. File handling is always the bottleneck — validate early, stream always, clean up immediately

  6. Mobile users are the majority — test on real devices, not DevTools simulation

  7. Every addEventListener Without a cleanup is a future memory leak

Tags#Next.js performance optimisation#Next.js route groups#TipTap editor optimisation#TipTap SSR hydration fix#server-side pagination Next.js#admin dashboard performance#FastAPI performance tips#async file processing FastAPI#file upload optimisation#streaming file uploads#Next.js App Router performance#React Server Components performance#dynamic import ssr false#memory leak React useEffect#useEffect cleanup pattern#Cloudinary Next.js optimisation#next/image performance#WebP AVIF optimisation#Core Web Vitals admin dashboard#LCP optimisation Next.js#CLS prevention images#table bloat pagination#Go backend performance#frontend performance 2026#admin panel optimisation#image placeholder Next.js#CDN asset delivery#bundle size optimisation Next.js#online tools performance#Snappy-Fix performance engineering
S

Written by

Snappy‑Fix Team

Part of the Snappy‑Fix team — building high‑performance websites, tools, and digital products.

Newsletter

Want more insights like this?

Get the latest articles, guides, and product updates from Snappy‑Fix delivered straight to your inbox.