How do I only wrap some pages in a Context Provider with Next.js?

Viewed 3260

I have three pages that I want to share data between (these are the core of the web app) but I also have a bunch of blog pages that don't care about that data. Everywhere I've looked suggests putting the Provider in the _app.tsx file. If I understand that correctly if I wrapp MyApp with the provider, if someone goes straight to www.myurl.com/blog/my-blog-1 (from google), that will cause the provider to run its functions; even if that page won't call useContext

How do I only wrap three pages in the Provider and leave out the blog pages?

For example:

Here's what my _app.tsx looks like:

import { AppProps } from 'next/app'
import '../styles/index.css'

export default function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}
5 Answers

A bit late to the party but there is actually an official way to do this: Use a per page layout. First slightly tweak your _app.tsx file:

import type { ReactElement, ReactNode } from 'react';
import { AppProps } from 'next/app';
import type { NextPage } from 'next';

type NextPageWithLayout = NextPage & {
    getLayout?: (page: ReactElement) => ReactNode;
};

type AppPropsWithLayout = AppProps & {
    Component: NextPageWithLayout;
};
export const App = ({ Component, pageProps }: AppPropsWithLayout): unknown => {

    // Use the custom layout defined at the page level, if available
    const getLayout = Component.getLayout ?? ((page) => page);

    return getLayout(<Component {...pageProps} />);
};

Then in a Page component write the following:

// DashboardPage.ts that need to have a UserProvider and CompanyProvider
import Layout from '@components/Layout'; // Default Layout that is imported for all the page
import { ReactElement } from 'react';
import { UserProvider } from '@contexts/user';
import { CompanyProvider } from '@contexts/company';

const DashboardPage = (): JSX.Element => {
    return <DashboardContainer />;
};

// Custom Layout to wrap the page within the User and company providers
DashboardPage.getLayout = function getLayout(page: ReactElement) {
    return (
        <Layout title="Dashboard page">
            <UserProvider>
                <CompanyProvider>{page}</CompanyProvider>
            </UserProvider>
        </Layout>
    );
};

export default DashboardPage;

Well, this is an interesting issue.

Challenge is in the way Next implements file-based routing. Since there is no way to create a wrapper for the group of pages only out-of-the-box thing you can do is to wrap the App in the context providers. But that doesn't really resolve your issue.

SOOO... I think there is a workaround. If you want to be able to wrap a certain group of pages in the context provider, first, you need to replace the file-based router with the react-router.

There is a very interesting article on this topic by Andrea Carraro. I think you should try this out: https://dev.to/toomuchdesign/next-js-react-router-2kl8

I will try to find another solution as well, but let me know did this worked for you.

There is workaround to wrap components of a specific path with a wrapper without react-router. Check out this video: https://www.youtube.com/watch?v=69-mnojSa0M

The video shows you how to wrap components of a sub-path with a nested layout. Create a nested layout and wrap that that layout with your Context Provider.

I'm kind new to Next.js and looking for the same question. Maybe we can just use pathname and query from the useRouter hook and try some conditionals in our Contexts or in the custom _app

next/router

Related