Why Migrate

Next.js App Router is the future of Next.js development. It introduces React Server Components, nested layouts, streaming, and a more intuitive file-system routing model. While Pages Router continues to work, new features and optimizations are focused on App Router. At Nexis Limited, our websites and web applications have been migrated to App Router for improved performance and developer experience.

Key Differences

Server Components by Default

In App Router, all components are React Server Components by default. They run on the server, can directly access databases and APIs, and send only HTML to the client — no JavaScript bundle. Add "use client" directive only for components that need interactivity (event handlers, useState, useEffect). This fundamentally changes how you structure components.

File-System Routing Changes

  • Routes are defined by folders, not files. A route needs a page.tsx file inside a folder.
  • Layouts are defined with layout.tsx files that wrap child routes. Layouts persist across navigations — they do not re-render when child routes change.
  • Loading states use loading.tsx files for automatic Suspense boundaries.
  • Error handling uses error.tsx files for automatic error boundaries.
  • Metadata is defined with generateMetadata functions or metadata exports.

Data Fetching

Replace getServerSideProps, getStaticProps, and getStaticPaths:

  • Server component data fetching: Fetch data directly in server components using async/await. No special data fetching functions needed.
  • generateStaticParams: Replaces getStaticPaths for generating static routes.
  • Dynamic rendering: Server components that use dynamic data (cookies, headers, search params) automatically render dynamically.
  • Static rendering: Components with static data are automatically statically rendered at build time.

Migration Strategy

Incremental Migration

App Router and Pages Router coexist in the same project. Migrate routes one at a time:

  1. Create the app/ directory alongside the existing pages/ directory.
  2. Move the root layout (header, footer, providers) to app/layout.tsx.
  3. Migrate routes one by one, starting with simple static pages.
  4. Migrate dynamic routes and data-heavy pages last.
  5. Remove the pages/ directory once all routes are migrated.

Component Migration

Evaluate each component for server vs client rendering:

  • Components that only display data → Server Component (default). No changes needed.
  • Components with onClick, onChange, useState, useEffect → Client Component. Add "use client" directive.
  • Components that mix static layout with interactive elements → Split into a server component wrapper with client component children.

Common Pitfalls

Overusing "use client"

Marking a component as "use client" makes it and all its children client components. Push the "use client" boundary as deep as possible — wrap only the interactive parts, not entire page sections. A page with a static header and an interactive form should have the header as a server component and only the form as a client component.

Passing Non-Serializable Props

Server components pass props to client components across the server-client boundary. Props must be serializable (JSON-compatible). Functions, class instances, and DOM elements cannot be passed. Use server actions for function-like communication from client to server.

Third-Party Library Compatibility

Many React libraries use client-side features (context, state, effects) and need the "use client" directive. If a third-party component does not work in a server component, wrap it in a client component. Check library documentation for App Router compatibility.

Caching Behavior

App Router has aggressive caching by default. Fetch requests are cached, router navigations are cached, and full route segments are cached. Understanding and configuring cache behavior (revalidation, cache tags, dynamic rendering) is essential for correct behavior.

Metadata and SEO

Replace the Head component from next/head with the Metadata API:

  • Static metadata: Export a metadata object from page.tsx or layout.tsx.
  • Dynamic metadata: Export a generateMetadata async function that fetches data for dynamic titles and descriptions.
  • The metadata API handles title templates, Open Graph images, canonical URLs, and robots directives.

Conclusion

Migrating to App Router is a significant but worthwhile investment. The server-first model reduces client JavaScript, improves performance, and simplifies data fetching. Migrate incrementally, understand the server/client component boundary, and test thoroughly at each step. The result is a faster, more maintainable application.

Planning your Next.js migration? Our frontend team has migrated multiple projects to App Router.