Back to Blog

Next.js 16 App Router: Ultimate UI Development Guide

The UI Factory

UI Patterns for Next.js 16 App Router

The Next.js App Router shifted the paradigm by making everything a Server Component by default. This is incredible for performance and SEO, but it created a headache for UI developers who rely on interactivity (React hooks, Framer Motion, window events).

Here is how we handle this architecture at The UI Factory.

The "use client" Boundary

The golden rule: Keep your client components as low in the tree as possible.

Do not wrap your entire page in "use client" just because you have a button that uses useState. Instead, isolate the button.

// src/components/ui/animated-button.tsx
"use client";

import { motion } from "framer-motion";

export const AnimatedButton = () => {
  return (
    <motion.button whileHover={{ scale: 1.1 }}>
      Interactive Client Component
    </motion.button>
  );
}

Then, import it into your Server Component:

// src/app/page.tsx
// This is a Server Component!
import { AnimatedButton } from "@/components/ui/animated-button";

export default function Home() {
  return (
    <main className="p-8">
      <h1>Server Rendered SEO Hero</h1>
      <AnimatedButton />
    </main>
  );
}

Hydration Mismatches with Framer Motion

When using complex physical animations, you might encounter hydration errors if you calculate styles using window.innerWidth before the component mounts.

Solution: Always use a mounted state flag or useEffect for calculations that depend on the DOM.

const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

if (!isMounted) return null; // Avoid rendering complex math on the server

By respecting these boundaries, you get the best of both worlds: perfect SEO from Next.js Server Components, and buttery smooth client-side interactions.