Next.js Lesson 9 – Dynamic Routing | Dataplexa
Lesson 9

Dynamic Routing

Build flexible page routes that adapt to user data — from article URLs to category filters

Dynamic routing transforms static websites into flexible applications. Think of it as the difference between a paper magazine with fixed pages and a digital platform that creates new pages on demand. Every time someone visits a unique URL, your application can generate the perfect page for that specific request. NewsWave needs dynamic routes everywhere. Article pages like /articles/breaking-ai-news and category filters like /category/tech cannot exist as static files. You would need thousands of individual page files. Dynamic routing solves this by creating one template that works for any article slug or category name. Unlike React Router where you define routes in JavaScript, Next.js uses the file system itself as your routing configuration. Square brackets in file names tell Next.js "this part of the URL can change." The magic happens automatically — no route configuration files or complex setup required.

Route Parameters

Route parameters capture the dynamic parts of URLs. When a user visits /articles/covid-vaccine-update, the parameter covid-vaccine-update gets passed to your page component. Your code can then use this parameter to fetch the right article, display the correct content, or make database queries. Parameters work like variables in a URL. Instead of hardcoding every possible value, you create a pattern that matches any value in that position. The file name determines the parameter name — a file called [slug].js creates a parameter called slug. A file called [id].js creates a parameter called id. Real applications use this pattern constantly. GitHub uses github.com/[username]/[repository] for user profiles and repositories. Medium uses medium.com/@[author]/[article-slug] for article pages. The same principle applies to e-commerce sites, documentation, and any application with user-generated content.
📁 app
  📁 articles
    📄 [slug]
      📄 page.js ← matches /articles/any-article-title
  📁 category
    📄 [name]
      📄 page.js ← matches /category/tech or /category/sports
  📁 search
    📄 page.js ← static route /search

Creating Your First Dynamic Route

Start with the NewsWave article page. Every news article needs its own URL based on the article title or slug. Create the folder structure first, then build the page component that receives the dynamic parameter.
# Create the articles folder with dynamic route
mkdir app/articles
mkdir app/articles/[slug]
touch app/articles/[slug]/page.js
Terminal
$ mkdir -p app/articles/[slug] && touch app/articles/[slug]/page.js
Created dynamic route structure
✓ Ready for article pages
Now create the article page component. The params prop contains the dynamic route parameters automatically passed by Next.js.
// app/articles/[slug]/page.js
// Dynamic article page that shows different content based on URL slug
export default function ArticlePage({ params }) {
  // params.slug contains the URL parameter (everything after /articles/)
  const articleSlug = params.slug;
  
  return (
    <div style={{ maxWidth: '800px', margin: '0 auto', padding: '40px 20px' }}>
      <h1 style={{ color: '#0f172a', fontSize: '32px', marginBottom: '16px' }}>
        Article: {articleSlug.replace(/-/g, ' ')}
      </h1>
      <p style={{ color: '#6b7280', marginBottom: '24px' }}>
        URL parameter captured: {articleSlug}
      </p>
    </div>
  );
}
localhost:3000/articles/breaking-tech-news — NewsWave
What just happened?
Next.js extracted "breaking-tech-news" from the URL and passed it as params.slug to your component. The square brackets in the folder name told Next.js this part of the route is dynamic. Try this: visit different URLs like /articles/covid-update or /articles/sports-news to see the parameter change.

Real Article Data Integration

Static text demonstrations help understand the concept, but real applications need actual data. NewsWave articles should display authentic content based on the URL parameter. Create a mock database of articles that your dynamic route can query and display.
// Mock article database (normally this would be a real database)
const articles = {
  'breaking-ai-news': {
    title: 'Breaking: AI Breakthrough Changes Everything',
    author: 'Sarah Chen',
    date: '2024-01-15',
    category: 'Tech',
    views: 2847,
    content: 'Revolutionary AI system demonstrates unprecedented capabilities...'
  },
  'climate-summit-2024': {
    title: 'Global Climate Summit Reaches Historic Agreement',
    author: 'Michael Rodriguez',
    date: '2024-01-14', 
    category: 'World',
    views: 1923,
    content: 'World leaders unite on ambitious climate action plan...'
  }
};
// Updated article page with real data lookup
export default function ArticlePage({ params }) {
  const article = articles[params.slug]; // Look up article by slug
  
  // Handle case where article doesn't exist
  if (!article) {
    return (
      <div style={{ textAlign: 'center', padding: '80px 20px' }}>
        <h1 style={{ color: '#dc2626' }}>Article Not Found</h1>
        <p style={{ color: '#6b7280' }}>No article exists with slug: {params.slug}</p>
      </div>
    );
  }
  
  return (
    <article style={{ maxWidth: '800px', margin: '0 auto', padding: '40px 20px' }}>
      <h1 style={{ fontSize: '36px', color: '#0f172a', marginBottom: '20px' }}>
        {article.title}
      </h1>
    </article>
  );
}
localhost:3000/articles/breaking-ai-news — NewsWave
What just happened?
Your component now looks up real article data using the URL slug as a key. The conditional rendering handles missing articles gracefully. Try this: create more articles in your mock database and visit their URLs to see different content.

Category Pages

Category filtering represents another common dynamic routing pattern. Instead of creating separate pages for tech news, sports news, and business news, one dynamic route handles all categories. The category name in the URL determines which articles to display. Category pages differ from article pages because they show multiple items instead of a single piece of content. The dynamic parameter filters a collection rather than selecting a specific item. This pattern appears everywhere — shopping sites filter products by category, job boards filter listings by industry, and social media apps filter posts by topic. NewsWave needs category pages that feel fast and responsive. Users clicking "Tech" should immediately see technology articles without waiting for slow database queries. The category parameter lets you pre-filter content and create focused experiences for different types of readers.
# Create category dynamic route structure  
mkdir app/category
mkdir app/category/[name]
touch app/category/[name]/page.js
Terminal
$ mkdir -p app/category/[name] && touch app/category/[name]/page.js
Category route structure created
✓ Ready for dynamic category filtering
// Mock articles database expanded with categories
const allArticles = [
  { slug: 'breaking-ai-news', title: 'AI Breakthrough Changes Everything', category: 'tech', views: 2847 },
  { slug: 'climate-summit-2024', title: 'Climate Summit Reaches Agreement', category: 'world', views: 1923 },
  { slug: 'quantum-computing-advance', title: 'Quantum Computing Milestone Achieved', category: 'tech', views: 1456 },
  { slug: 'olympic-records-broken', title: 'Multiple Olympic Records Fall', category: 'sports', views: 3421 },
  { slug: 'market-volatility-continues', title: 'Stock Market Sees Major Swings', category: 'business', views: 2156 }
];
// app/category/[name]/page.js  
// Dynamic category page that filters articles by category name
export default function CategoryPage({ params }) {
  const categoryName = params.name; // Get category from URL parameter
  
  // Filter articles that match the category parameter
  const categoryArticles = allArticles.filter(
    article => article.category.toLowerCase() === categoryName.toLowerCase()
  );
  
  return (
    <div style={{ maxWidth: '1000px', margin: '0 auto', padding: '40px 20px' }}>
      <h1 style={{ fontSize: '32px', color: '#0f172a', marginBottom: '8px' }}>
        {categoryName.charAt(0).toUpperCase() + categoryName.slice(1)} News
      </h1>
      <p style={{ color: '#6b7280', marginBottom: '32px' }}>
        {categoryArticles.length} articles found
      </p>
    </div>
  );
}
localhost:3000/category/tech — NewsWave

Multiple Route Parameters

Advanced applications often need multiple dynamic segments in a single URL. Consider a news site with URLs like /category/tech/2024/january for monthly archives, or an e-commerce site with /products/electronics/smartphones/brand/apple for nested product filtering. Multiple parameters create powerful URL structures that feel intuitive to users and search engines. Each segment adds another layer of organization and filtering capability. The challenge lies in managing the complexity — more parameters mean more edge cases and validation requirements. NewsWave could benefit from date-based routing for archived content. Users browsing older articles might prefer URLs like /archive/2023/december rather than endless pagination. This pattern also helps with SEO since search engines can better understand content organization.
# Create nested dynamic route with multiple parameters
mkdir -p app/archive/[year]/[month]  
touch app/archive/[year]/[month]/page.js
Terminal
$ mkdir -p app/archive/[year]/[month] && touch app/archive/[year]/[month]/page.js
Nested dynamic route created
✓ Multiple parameters ready
// app/archive/[year]/[month]/page.js
// Archive page with year and month parameters
export default function ArchivePage({ params }) {
  const { year, month } = params; // Destructure both parameters
  
  // Mock function to get articles for specific year/month
  const getArchiveArticles = (year, month) => {
    // In real app, this would query database with date filters
    return allArticles.filter(article => {
      // Simulate date filtering logic
      return Math.random() > 0.3; // Mock: randomly show some articles
    });
  };
  
  const archiveArticles = getArchiveArticles(year, month);
  
  return (
    <div style={{ maxWidth: '1000px', margin: '0 auto', padding: '40px 20px' }}>
      <h1 style={{ fontSize: '32px', color: '#0f172a', marginBottom: '16px' }}>
        NewsWave Archive: {month.charAt(0).toUpperCase() + month.slice(1)} {year}
      </h1>
    </div>
  );
}
localhost:3000/archive/2023/december — NewsWave
What just happened?
Next.js passed both URL segments as separate parameters in the params object. The nested folder structure [year]/[month] created a two-level dynamic route that matches URLs like /archive/2023/december. Try this: visit different year/month combinations to see both parameters change.

Catch-All Routes

Some applications need routes that match any number of URL segments. Documentation sites often use paths like /docs/api/routes/dynamic or /docs/getting-started/installation/setup where the depth varies. Catch-all routes solve this with a special syntax that captures everything after a certain point. The triple-dot syntax [...slug] tells Next.js to match one or more segments and pass them as an array. A file named [...slug].js matches /docs/api, /docs/api/routes, and /docs/api/routes/dynamic/advanced. The parameter becomes an array like ['api', 'routes', 'dynamic', 'advanced']. This pattern works perfectly for content management systems, documentation platforms, and any application with hierarchical content. NewsWave could use catch-all routes for topic hierarchies like /topics/technology/artificial-intelligence/machine-learning where users can browse increasingly specific content areas.
# Create catch-all route for topics  
mkdir -p app/topics/[...path]
touch app/topics/[...path]/page.js
Terminal
$ mkdir -p app/topics/[...path] && touch app/topics/[...path]/page.js
Catch-all route structure created
✓ Variable depth routing ready
// app/topics/[...path]/page.js
// Catch-all route that handles any number of path segments
export default function TopicsPage({ params }) {
  const pathSegments = params.path; // Array of all URL segments after /topics/
  
  // Create breadcrumb navigation from path segments
  const breadcrumbs = pathSegments.map(segment => 
    segment.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())
  );
  
  return (
    <div style={{ maxWidth: '1000px', margin: '0 auto', padding: '40px 20px' }}>
      <nav style={{ marginBottom: '24px' }}>
        <span style={{ color: '#6b7280' }}>Topics > </span>
        {breadcrumbs.map((crumb, index) => (
          <span key={index} style={{ color: index === breadcrumbs.length - 1 ? '#0369a1' : '#6b7280' }}>
            {crumb}
            {index < breadcrumbs.length - 1 && <span style={{ color: '#6b7280' }}> > </span>}
          </span>
        ))}
      </nav>
    </div>
  );
}
localhost:3000/topics/technology/artificial-intelligence/machine-learning — NewsWave

Route Priority and Conflicts

Multiple route patterns can match the same URL, creating conflicts that Next.js must resolve. Understanding route priority prevents unexpected behavior and helps you structure applications correctly. The file system routing follows specific rules to determine which route wins when multiple patterns match the same path. Static routes always beat dynamic routes. A file called app/articles/featured/page.js takes priority over app/articles/[slug]/page.js when someone visits /articles/featured. Dynamic routes beat catch-all routes. Single parameter routes like [id] beat catch-all routes like [...path]. Route conflicts become critical in large applications. NewsWave needs special pages like /articles/submit for article submission and /articles/trending for popular content. Without understanding priority, these might conflict with the dynamic /articles/[slug] route.
Route Priority Rules
1. Static routes beat dynamic routes
2. Dynamic routes beat catch-all routes
3. Named catch-all [...slug] beats optional [[...slug]]
4. More specific routes beat less specific ones
// Example of route priority in NewsWave
// File structure that handles conflicts correctly:

// app/articles/trending/page.js     ← Static route (highest priority)
// app/articles/submit/page.js       ← Static route (highest priority)  
// app/articles/[slug]/page.js       ← Dynamic route (medium priority)
// app/articles/[...path]/page.js    ← Catch-all route (lowest priority)

// URL /articles/trending → goes to trending/page.js (static wins)
// URL /articles/breaking-news → goes to [slug]/page.js (dynamic matches)
// URL /articles/tech/ai/deep → goes to [...path]/page.js (catch-all matches)
What just happened?
Next.js evaluates routes from most specific to least specific. Static paths like "trending" always match first, then single dynamic parameters, then catch-all routes. This prevents dynamic routes from accidentally capturing URLs meant for specific pages. Plan your route structure to avoid conflicts.
Dynamic routing opens unlimited possibilities for flexible web applications. Your NewsWave site can now handle any article URL, filter by categories, browse archives by date, and explore topics at any depth. The file-based routing system makes these complex patterns feel natural and predictable. Real applications combine multiple routing patterns strategically. Static routes handle special pages, single dynamic routes manage individual content items, and catch-all routes provide flexible navigation structures. The key lies in understanding how they work together and planning your URL structure accordingly.

Quiz

1. NewsWave has a file at app/articles/[slug]/page.js. When a user visits /articles/breaking-tech-news, what happens?


2. You want NewsWave to handle URLs like /category/tech, /category/sports, and /category/business with one component. What file structure do you create?


3. NewsWave has both app/articles/featured/page.js and app/articles/[slug]/page.js. What happens when someone visits /articles/featured?


Up Next: API Routes

Transform NewsWave into a full-stack application by building custom API endpoints that handle data, authentication, and server-side logic.