Skip to content

Next.js Route Groups

Next.js Route Groups

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Starting from Next.js 13.4, Vercel introduced the App Router with a whole set of new and exciting features. The way we organize the routing in our application has changed radically compared to previous versions of Next.js, as well as the definition and usage of Layouts for our pages. In this article, we will focus on what is called Route Groups, their use cases, and how they can help us in our developer experience.

Basic introduction to the new App Router

In version 13, Next.js introduced a new App Router built on React Server Components, which supports shared layouts, nested routing, loading states, error handling, and more.

The App Router works in a new directory named app

The App Router works in a new directory named app

Creating a page.tsx file inside the app/test-page folder allows you to define what users are going to see when they navigate to /test-page. So folder’s names inside app directory define your app routes.

You can also have nested routes like this:

nested routes

In this case, the URL of your page will be /test-page/nested-page

By default, components inside app are React Server Components. This is a performance optimization and allows you to easily adopt them, and you can also use Client Components.

Layouts

In Next.js, a Layout file is a special component that is used to define the common structure and layout of multiple pages in your application. It acts as a wrapper around the content of each page, providing consistent styling, structure, and functionality.

The purpose of a Layout file is to encapsulate shared elements such as headers, footers, navigation menus, sidebars, or any other components that should be present on multiple pages. By using a Layout file, you can avoid duplicating code across multiple pages and ensure a consistent user experience throughout your application.

To create a Layout file in Next.js, you typically create a separate component file, such as Layout.tsx, and define the desired layout structure within it. This component can then be imported and used on individual pages where you want to apply the shared layout.

By wrapping your page content with the Layout component, Next.js will render the shared layout around each page, providing a consistent look and feel. This approach simplifies the management of common elements and allows for easy updates or modifications to the layout across multiple pages.

Here is an example of how to use a Layout file with the new App Router

the new App Router 3
the new App Router 4

Route Groups

In Next.js, the folders in your app directory usually correspond to URL paths. But if you mark a folder as a Route Group, it won't be included in the URL path of the route.

This means you can organize your routes and project files into groups without changing the URL structure.

Route groups are helpful for:

  1. Organizing routes into groups based on site sections, intent, or teams.

  2. Creating nested layouts within the same route segment level:

    • You can have multiple nested layouts in the same segment, even multiple root layouts.

    • You can add a layout to only a subset of routes within a common segment.

To create a route group inside your app folder you just need to wrap the folder’s name in parenthesis: (folderName)

To create a route group inside your app folder you just need to wrap the folder’s name in parenthesis: (folderName)

Since route groups won’t change the URL structure your page.tsx content will be shown under the/inside-route-group path.

Use cases

Route groups are amazing when you want to create multiple layouts inside your page:

Route groups are amazing when you want to create multiple layouts inside your page

Or if you want to specify a layout for a specific group of pages

specify a layout for a specific group of pages

You need to be careful because all the examples above can lead you to some misunderstanding.

What is root layout? The top-most layout is called the Root Layout. This required layout is shared across all pages in an application.

As you can see, the route folder in the two examples above always has a well-defined root layout. This means that the specific layouts we have defined for the various groups will not replace the root layout, but will be added to it. However, Route Groups also allow us to redefine the root layout. Specifically, they allow us to define different root layouts for different segments of pages.

Route Groups

All we have to do is remove the common Root Layout file, create some Route groups, and re-define the different Root layout files for every group in the route folder:

create some Route groups

In this way, we will have pages with different root layouts, and our paths will once again not be affected by the folder name used in parentheses.

pages with different root layouts
our paths will once again not be affected by the folder name used in parentheses

Conclusion

In conclusion, Next.js route groups offer a powerful and flexible solution for organizing and managing routes in your Next.js applications. By grouping related routes together, you can improve code organization, enhance maintainability, and promote code reusability. Route groups allow for the use of shared layout components, and the customization of root layouts for different segments of pages. With Next.js route groups, you can streamline your development process, create a more intuitive routing structure, and ultimately deliver a better user experience.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Internationalization in Next.js with next-intl cover image

Internationalization in Next.js with next-intl

Internationalization in Next.js with next-intl Internationalization (i18n) is essential for providing a multi-language experience for global applications. next-intl integrates well with Next.js’ App Router, handling i18n routing, locale detection, and dynamic configuration. This guide will walk you through setting up i18n in Next.js using next-intl for URL-based routing, user-specific settings, and domain-based locale routing. Getting Started First, create a Next.js app with the App Router and install next-intl: ` Next, configure next-intl in the next.config.ts file to provide a request-specific i18n configuration for Server Components: ` Without i18n Routing Setting up an app without i18n routing integration can be advantageous in scenarios where you want to provide a locale to next-intl based on user-specific settings or when your app supports only a single language. This approach offers the simplest way to begin using next-intl, as it requires no changes to your app’s structure, making it an ideal choice for straightforward implementations. ` Here’s a quick explanation of each file's role: * translations/: Stores different translations per language (e.g., en.json for English, es.json for Spanish). Organize this as needed, e.g., translations/en/common.json. * request.ts: Manages locale-based configuration scoped to each request. Setup request.ts for Request-Specific Configuration Since we will be using features from next-intl in Server Components, we need to add the following configuration in i18n/request.ts: ` Here, we define a static locale and use that to determine which translation file to import. The imported JSON data is stored in the message variable, and is returned together with the locale so that we can access them from various components in the application. Using Translation in RootLayout Inside RootLayout, we use getLocale() to retrieve the static locale and set the document language for SEO and pass translations to NextIntlClientProvider: ` Note that NextIntlClientProvider automatically inherits configuration from i18n/request.ts here, but messages must be explicitly passed. Now you can use translations and other functionality from next-intl in your components: ` In case of async components, you can use the awaitable getTranslations function instead: ` And with that, you have i18n configured and working on your application! \ Now, let’s take it a step further by introducing routing. \ With i18n Routing To set up i18n routing, we need a file structure that separates each language configuration and translation file. Below is the recommended structure: ` We updated the earlier structure to include some files that we require for routing: * routing.ts: Sets up locales, default language, and routing, shared between middleware and navigation. * middleware.ts: Handles URL rewrites and locale negotiation. * app/[locale]/: Creates dynamic routes for each locale like /en/about and /es/about. Define Routing Configuration in i18n/routing.ts The routing.ts file configures supported locales and the default locale, which is referenced by middleware.ts and other navigation functions: ` This configuration lets Next.js handle URL paths like /about, with locale management managed by next-intl. Update request.ts for Request-Specific Configuration We need to update the getRequestConfig function from the above implementation in i18n/request.ts. ` Here, request.ts ensures that each request loads the correct translation files based on the user’s locale or falls back to the default. Setup Middleware for Locale Matching The middleware.ts file matches the locale based on the request: ` Middleware handles locale matches and redirects to localized paths like /en or /es. Updating the RootLayout file Inside RootLayout, we use the locale from params (matched by middleware) instead of calling getLocale() ` The locale we get from the params was matched in the middleware.ts file and we use that here to set the document language for SEO purposes. Additionally, we used this file to pass configuration from i18n/request.ts to Client Components through NextIntlClientProvider. Note: When using the above setup with i18n routing, next-intl will currently opt into dynamic rendering when APIs like useTranslations are used in Server Components. next-intl provides a temporary API that can be used to enable static rendering. Static Rendering for i18n Routes For apps with dynamic routes, use generateStaticParams to pass all possible locale values, allowing Next.js to render at build time: ` next-intl provides an API setRequestLocale that can be used to distribute the locale that is received via params in layouts and pages for usage in all Server Components that are rendered as part of the request. You need to call this function in every layout/page that you intend to enable static rendering for since Next.js can render layouts and pages independently. ` Note: Call setRequestLocale before invoking useTranslations or getMessages or any next-intl functions. Domain Routing For domain-specific locale support, use the domains setting to map domains to locales, such as us.example.com/en or ca.example.com/fr. ` This setup allows you to serve localized content based on domains. Read more on domain routing here. Conclusion Setting up internationalization in Next.js with next-intl provides a modular way to handle URL-based routing, user-defined locales, and domain-specific configurations. Whether you need URL-based routing or a straightforward single-locale setup, next-intl adapts to fit diverse i18n needs. With these tools, your app will be ready to deliver a seamless multi-language experience to users worldwide....

Vercel & React Native - A New Era of Mobile Development? cover image

Vercel & React Native - A New Era of Mobile Development?

Vercel & React Native - A New Era of Mobile Development? Jared Palmer of Vercel recently announced an acquisition that spiked our interest. Having worked extensively with both Next.js and Vercel, as well as React Native, we were curious to see what the appointment of Fernando Rojo, the creator of Solito, as Vercel's Head of Mobile, would mean for the future of React Native and Vercel. While we can only speculate on what the future holds, we can look closer at Solito and its current integration with Vercel. Based on the information available, we can also make some educated guesses about what the future might hold for React Native and Vercel. What is Solito? Based on a recent tweet by Guillermo Rauch, one might assume that Solito allows you to build mobile apps with Next.js. While that might become a reality in the future, Jamon Holmgren, the CTO of Infinite Red, added some context to the conversation. According to Jamon, Solito is a cross-platform framework built on top of two existing technologies: - For the web, Solito leverages Next.js. - For mobile, Solito takes advantage of Expo. That means that, at the moment, you can't build mobile apps using Next.js & Solito only - you still need Expo and React Native. Even Jamon, however, admits that even the current integration of Solito with Vercel is exciting. Let's take a closer look at what Solito is according to its official website: > This library is two things: > > 1. A tiny wrapper around React Navigation and Next.js that lets you share navigation code across platforms. > > 2. A set of patterns and examples for building cross-platform apps with React Native + Next.js. We can see that Jamon was right - Solito allows you to share navigation code between Next.js and React Native and provides some patterns and components that you can use to build cross-platform apps, but it doesn't replace React Native or Expo. The Cross-Platformness of Solito So, we know Solito provides a way to share navigation and some patterns between Next.js and React Native. But what precisely does that entail? Cross-Platform Hooks and Components If you look at Solito's documentation, you'll see that it's not only navigation you can share between Next.js and React Native. There are a few components that wrap Next.js components and make them available in React Native: - Link - a component that wraps Next.js' Link component and allows you to navigate between screens in React Native. - TextLink - a component that also wraps Next.js' Link component but accepts text nodes as children. - MotiLink - a component that wraps Next.js' Link component and allows you to animate the link using moti, a popular animation library for React Native. - SolitoImage - a component that wraps Next.js' Image component and allows you to display images in React Native. On top of that, Solito provides a few hooks that you can use for shared routing and navigation: - useRouter() - a hook that lets you navigate between screens across platforms using URLs and Next.js Url objects. - useLink() - a hook that lets you create Link components across the two platforms. - createParam() - a function that returns the useParam() and useParams() hooks which allow you to access and update URL parameters across platforms. Shared Logic The Solito starter project is structured as a monorepo containing: - apps/next - the Next.js application. - apps/expo or apps/native - the React Native application. - packages/app - shared packages across the two applications: - features - providers - navigation The shared packages contain the shared logic and components you can use across the two platforms. For example, the features package contains the shared components organized by feature, the providers package contains the shared context providers, and the navigation package includes the shared navigation logic. One of the key principles of Solito is gradual adoption, meaning that if you use Solito and follow the recommended structure and patterns, you can start with a Next.js application only and eventually add a React Native application to the mix. Deployments Deploying the Next.js application built on Solito is as easy as deploying any other Next.js application. You can deploy it to Vercel like any other Next.js application, e.g., by linking your GitHub repository to Vercel and setting up automatic deployments. Deploying the React Native application built on top of Solito to Expo is a little bit more involved - you cannot directly use the Github Action recommended by Expo without some modification as Solito uses a monorepo structure. The adjustment, however, is luckily just a one-liner. You just need to add the working-directory parameter to the eas update --auto command in the Github Action. Here's what the modified part of the Expo Github Action would look like: ` What Does the Future Hold? While we can't predict the future, we can make some educated guesses about what the future might hold for Solito, React Native, Expo, and Vercel, given what we know about the current state of Solito and the recent acquisition of Fernando Rojo by Vercel. A Competitor to Expo? One question that comes to mind is whether Vercel will work towards creating a competitor to Expo. While it's too early to tell, it's not entirely out of the question. Vercel has been expanding its offering beyond Next.js and static sites, and it's not hard to imagine that it might want to provide a more integrated, frictionless solution for building mobile apps, further bridging the gap between web and mobile development. However, Expo is a mature and well-established platform, and building a mobile app toolchain from scratch is no trivial task. It would be easier for Vercel to build on top of Expo and partner with them to provide a more integrated solution for building mobile apps with Next.js. Furthermore, we need to consider Vercel's target audience. Most of Vercel's customers are focused on web development with Next.js, and switching to a mobile-first approach might not be in their best interest. That being said, Vercel has been expanding its offering to cater to a broader audience, and providing a more integrated solution for building mobile apps might be a step in that direction. A Cross-Platform Framework for Mobile Apps with Next.js? Imagine a future where you write your entire application in Next.js — using its routing, file structure, and dev tools — and still produce native mobile apps for iOS and Android. It's unlikely such functionality would be built from scratch. It would likely still rely on React Native + Expo to handle the actual native modules, build processes, and distribution. From the developer’s point of view, however, it would still feel like writing Next.js. While this idea sounds exciting, it's not likely to happen in the near future. Building a cross-platform framework that allows you to build mobile apps with Next.js only would require a lot of work and coordination between Vercel, Expo, and the React Native community. Furthermore, there are some conceptual differences between Next.js and React Native that would need to be addressed, such as Next.js being primarily SSR-oriented and native mobile apps running on the client. Vercel Building on Top of Solito? One of the more likely scenarios is that Vercel will build on top of Solito to provide a more integrated solution for building mobile apps with Next.js. This could involve providing more components, hooks, and patterns for building cross-platform apps, as well as improving the deployment process for React Native applications built on top of Solito. A potential partnership between Vercel and Expo, or at least some kind of closer integration, could also be in the cards in this scenario. While Expo already provides a robust infrastructure for building mobile apps, Vercel could provide complementary services or features that make it easier to build mobile apps on top of Solito. Conclusion Some news regarding Vercel and mobile development is very likely on the horizon. After all, Guillermo Rauch, the CEO of Vercel, has himself stated that Vercel will keep raising the quality bar of the mobile and web ecosystems. While it's unlikely we'll see a full-fledged mobile app framework built on top of Next.js or a direct competitor to Expo in the near future, it's not hard to imagine that Vercel will provide more tools and services for building mobile apps with Next.js. Solito is a step in that direction, and it's exciting to see what the future holds for mobile development with Vercel....

Mastering Git Rerere: Solving Repetitive Merge Conflicts with Ease cover image

Mastering Git Rerere: Solving Repetitive Merge Conflicts with Ease

Mastering Git Rerere: Solving Repetitive Merge Conflicts with Ease Introduction: Git, the popular version control system, has revolutionized how developers collaborate on projects. However, one common pain point in Git is repetitive merge conflicts. Fortunately, Git provides a powerful and not-so-well-known solution called git rerere (reuse recorded resolution) that can save you time and effort when resolving conflicts. In this blog post, we will explore how to configure and use git rerere, and demonstrate its effectiveness in solving merge conflicts. Understanding Git Rerere: Git rerere is a feature that allows Git to remember how you resolved a particular merge conflict in a particular file and automatically apply the same resolution in the future. It works by recording the conflict resolution in a hidden directory called .git/rr-cache. This way, when Git encounters the same conflict in the same file in the future, it can reuse the recorded resolution, saving you from manually resolving the conflict again. Configuring Git Rerere: Before using git rerere, you need to enable it in your Git configuration; only once git rerere has been enabled, Git will start recording and remembering resolved conflicts.Open your terminal and run the following command: ` This command enables git rerere globally, making it available for all your repositories. You can also enable it per-repository by omitting the --global flag. How to use it: Let's start with a really easy example, then describe a couple of use cases. We have merged a branch (branch-one) into our main, and we are working on two different features in two different branches (branch-two and branch-three). We need to rebase our branches with main, so we start with ` It turns out that there are some conflicts on App.tsx file: photo1.png We solve all the conflicts and finish with the rebase and push. As you can see, there are an extra line in the rebase output message that says: This means that thanks to rerere option enabled, we have saved in our project's .git/rr-cache this resolution for this particular conflict. Now let's switch branches into branch-three cause we want to rebase on main also this one: ` It seems that we have the same conflicts here too, but this time, on the rebase output message, we can read: The conflict has been resolved automatically; if we check our IDE, we can see the change (and check if it works for us) ready to be committed and pushed with the same resolution we manually used in the past rebase. The example above focuses on a rebase, but of course, git rerere also works for conflicts that came out from a merge command. Here are a couple of real-life scenarios where git rerere can save the day: Frequent Integration of Feature Branches: Imagine you're working on a feature branch that frequently needs to be merged into the main development branch. Each time you merge, you encounter the same merge conflicts. With git rerere, you only need to resolve these conflicts once. After that, Git remembers the resolutions and automatically applies them in future merges, saving you from resolving the same conflicts repeatedly. Reapplying Patches or Fixes: Let's say you have a situation where you need to apply the same set of changes or fixes to multiple branches. When you encounter conflicts during this process, git rerere can remember how you resolved them the first time. Then, when you apply the changes to other branches, Git can automatically reuse the recorded resolutions, sparing you from manually resolving the same conflicts repeatedly. Benefits of Git Rerere: Git rerere offers several benefits that make it a valuable tool for developers, regardless of whether you prefer git merge or git rebase: 1. Time-saving: By reusing recorded resolutions, git rerere eliminates the need to manually resolve repetitive merge conflicts, saving you valuable time and effort. 2. Consistency: Git rerere ensures consistent conflict resolutions across multiple merges or rebases, reducing the chances of introducing errors or inconsistencies. 3. Improved productivity: With git rerere, you can focus on more critical tasks instead of getting stuck in repetitive conflict resolutions. Conclusion: Git rerere is a powerful feature that simplifies resolving repetitive merge conflicts. By enabling git rerere and recording conflict resolutions, you can save time, improve productivity, and ensure consistent conflict resolutions across your projects. Incorporate git rerere into your Git workflow, and say goodbye to the frustration of repetitive merge conflicts....

The simplicity of deploying an MCP server on Vercel cover image

The simplicity of deploying an MCP server on Vercel

The current Model Context Protocol (MCP) spec is shifting developers toward lightweight, stateless servers that serve as tool providers for LLM agents. These MCP servers communicate over HTTP, with OAuth handled clientside. Vercel’s infrastructure makes it easy to iterate quickly and ship agentic AI tools without overhead. Example of Lightweight MCP Server Design At This Dot Labs, we built an MCP server that leverages the DocuSign Navigator API. The tools, like `get_agreements`, make a request to the DocuSign API to fetch data and then respond in an LLM-friendly way. ` Before the MCP can request anything, it needs to guide the client on how to kick off OAuth. This involves providing some MCP spec metadata API endpoints that include necessary information about where to obtain authorization tokens and what resources it can access. By understanding these details, the client can seamlessly initiate the OAuth process, ensuring secure and efficient data access. The Oauth flow begins when the user's LLM client makes a request without a valid auth token. In this case they’ll get a 401 response from our server with a WWW-Authenticate header, and then the client will leverage the metadata we exposed to discover the authorization server. Next, the OAuth flow kicks off directly with Docusign as directed by the metadata. Once the client has the token, it passes it in the Authorization header for tool requests to the API. ` This minimal set of API routes enables me to fetch Docusign Navigator data using natural language in my agent chat interface. Deployment Options I deployed this MCP server two different ways: as a Fastify backend and then by Vercel functions. Seeing how simple my Fastify MCP server was, and not really having a plan for deployment yet, I was eager to rewrite it for Vercel. The case for Vercel: * My own familiarity with Next.js API deployment * Fit for architecture * The extremely simple deployment process * Deploy previews (the eternal Vercel customer conversion feature, IMO) Previews of unfamiliar territory Did you know that the MCP spec doesn’t “just work” for use as ChatGPT tooling? Neither did I, and I had to experiment to prove out requirements that I was unfamiliar with. Part of moving fast for me was just deploying Vercel previews right out of the CLI so I could test my API as a Connector in ChatGPT. This was a great workflow for me, and invaluable for the team in code review. Stuff I’m Not Worried About Vercel’s mcp-handler package made setup effortless by abstracting away some of the complexity of implementing the MCP server. It gives you a drop-in way to define tools, setup https-streaming, and handle Oauth. By building on Vercel’s ecosystem, I can focus entirely on shipping my product without worrying about deployment, scaling, or server management. Everything just works. ` A Brief Case for MCP on Next.js Building an API without Next.js on Vercel is straightforward. Though, I’d be happy deploying this as a Next.js app, with the frontend features serving as the documentation, or the tools being a part of your website's agentic capabilities. Overall, this lowers the barrier to building any MCP you want for yourself, and I think that’s cool. Conclusion I'll avoid quoting Vercel documentation in this post. AI tooling is a critical component of this natural language UI, and we just want to ship. I declare Vercel is excellent for stateless MCP servers served over http....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co