nextjs-shadcn-netlify-test: A Next.js boilerplate that treats hydration like a hostile environment

Nine shadcn theme files, a defensive body wrapper, and Netlify config tuned to make Next.js 15 behave.

10 min read • View on GitHub • More from aidenybai

A wide editorial scene shows a browser shell built like a house foundation, with the body tag as the concrete slab at the center. Loose mechanical braces tug at the frame while interchangeable theme panels wait off to the side, explaining that the repo is about keeping the shell stable while the visual system changes underneath.
The whole project is a study in keeping the shell stable while everything around it can change.
Key Takeaways

The most revealing line in aidenybai/nextjs-shadcn-netlify-test is the empty page. `page.tsx` returns nothing, which tells you the repository is not trying to impress users. It is trying to prove that a stack can survive theming, hydration, and deployment without drama.

This is a test bench, not a starter kit

The repo reads like an internal compatibility harness for a senior frontend engineer. Next.js 15, shadcn/ui, Tailwind, Biome, and Netlify all show up, but the interesting part is the choreography between them. Nothing here is decorative. Every file exists to answer a deployment or hydration question.

The body tag is the sharp edge

The sharpest code lives in `src/app/ClientBody.tsx`. Instead of trusting the server-rendered body class forever, the client restores a known value after hydration so browser extensions cannot leave the DOM in a mismatched state.

'use client';

import { useEffect } from 'react';

export function ClientBody() {
  useEffect(() => {
    document.body.className = 'antialiased';
  }, []);

  return null;
}

That is defensive engineering in one line. The app assumes the browser may alter `body` before React finishes hydrating, and it chooses consistency over preserving whatever happened to be there.

A close-up shows a rectangular metal plate seated in a frame, with one gloved hand pressing it flat and another hand brushing away stray marks from the edge. It explains why the repo rewrites the body class after hydration instead of trusting the browser to leave the DOM untouched.
The mismatch is small, but the failure mode is noisy.

Nine CSS files, one design system

The theme strategy is simple and unusually practical. Instead of forcing one palette into every context, the repo keeps separate `globals.[color].css` files, each one defining the same HSL variables for shadcn/ui. Swap the import, and the entire surface changes with it.

:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --accent: 210 40% 96.1%;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 210 40% 98%;
}

That matters because shadcn/ui is opinionated about tokens, not about brands. The repo uses that to make theme changes a file-level decision instead of a component rewrite.

The stack is small, but each layer has a specific job.

Netlify is the other half of the test

The deployment config is not just housekeeping. `next.config.js` changes the build output directory depending on environment, and `netlify.toml` skips the default Next plugin path while still opting into `@netlify/plugin-nextjs`. The message is blunt: this stack will work, but only if you tell every layer exactly what to do.

const nextConfig = {
  eslint: { ignoreDuringBuilds: true },
  typescript: { ignoreDuringBuilds: true },
  distDir: process.env.NODE_ENV === 'production' ? '.next' : '.next-dev',
};

That is a conscious tradeoff. It favors a reliable deploy signal over strict build-time enforcement, which makes sense in a repository whose job is to verify the plumbing more than police the codebase.


What it looks like compared with the defaults

ConcernCommon defaultThis repo's moveTradeoff
Theme surfaceOne global stylesheet or runtime theme swapNine preset CSS files with identical tokensMore files, less runtime branching
HydrationTrust the server markup and fix mismatches laterReset `body` on the client after hydrationSmall client-side repair for a browser-side problem
Build checksFail builds on lint and TypeScript errorsIgnore those checks during buildsFaster green builds, less build-time safety
HostingUse the host's default Next.js pathExplicit Netlify plugin and distDir wiringMore setup, fewer surprises

The pattern is useful anywhere the app shell matters more than the demo content. If you ship a theme-heavy product and want a deployment target that is not the framework's home turf, this is a clean, opinionated starting point.