Fetching Sanity site config in Next.js _app.tsx returns empty in pageProps

6 replies
Last updated: Mar 30, 2021
Morning all. I’m trying to get some site config props into the _app.tsx file (nextjs v10), like this old example does: https://github.com/sanity-io/sanity-template-nextjs-landing-pages . The doc from sanity holds menu items and some seo stuff. Aim is to build the navigation menu and set some <head> stuff.

pageProps.config = client.fetch(siteConfigQuery).then((config) => {
        return config;
    });
Any tips on this please? If i console log config, it’s populated but when <Component {…pageProps} /> is received ,it’s empty.


    const siteConfigQuery = groq`
 *[_type == "site-config"][0] {
    ...,
    logo {asset->{extension, url}},
    mainNavigation[] -> {
      ...,
      "title": page->title
    },
    footerNavigation[] -> {
      ...,
      "title": page->title
    }
  }
  `;

export default function MyApp(props: AppProps) {
    const { Component, pageProps } = props;

    React.useEffect(() => {
        // Remove the server-side injected CSS.
        const jssStyles = document.querySelector('#jss-server-side');
        if (jssStyles) {
            jssStyles.parentElement?.removeChild(jssStyles);
        }
    }, []);

    const router = useRouter();

    // Initiate GTM
    useEffect(() => {
        const handleRouteChange = (url: string) => GTMPageView(url);

        router.events.on('routeChangeComplete', handleRouteChange);
        return () => {
            router.events.off('routeChangeComplete', handleRouteChange);
        };
    }, []);

    // Add site config from sanity
    pageProps.config = client.fetch(siteConfigQuery).then((config) => {
        return config;
    });
    return (<>
        {/* <CacheProvider value={cache}> */}
            <Head>
                <title>The Togetherworks</title>
                <meta
                    name="viewport"
                    content="initial-scale=1, width=device-width"
                />
            </Head>
            <ThemeProvider theme={theme}>
                <CssBaseline />
                <GlobalStyles />
                <Component {...pageProps} />
            </ThemeProvider>
        {/* </CacheProvider> */}
        </>
    );
}
AI Update

The issue you're facing is that you're assigning a Promise to pageProps.config instead of the resolved data. When you do client.fetch(siteConfigQuery).then(...), that returns a Promise, not the actual config data.

The solution is to use getInitialProps in your _app.tsx to fetch the data server-side before rendering, just like that landing pages template does. Here's how to fix it:

import App, { AppContext, AppProps } from 'next/app';

export default function MyApp(props: AppProps) {
    const { Component, pageProps } = props;

    // Your existing useEffect hooks...

    return (
        <>
            <Head>
                <title>The Togetherworks</title>
                <meta name="viewport" content="initial-scale=1, width=device-width" />
            </Head>
            <ThemeProvider theme={theme}>
                <CssBaseline />
                <GlobalStyles />
                <Component {...pageProps} />
            </ThemeProvider>
        </>
    );
}

MyApp.getInitialProps = async (appContext: AppContext) => {
    // Fetch site config
    const config = await client.fetch(siteConfigQuery);
    
    // Call page-level getInitialProps if it exists
    const appProps = await App.getInitialProps(appContext);
    
    // Merge config into all pages' props
    return {
        ...appProps,
        pageProps: {
            ...appProps.pageProps,
            config
        }
    };
};

Important caveat: Adding getInitialProps to _app will disable Automatic Static Optimization for all your pages. This means pages using getStaticProps will lose some of their static optimization benefits, as Next.js will switch to server-side rendering for every request instead of serving pre-built static HTML.

Better alternatives for modern Next.js:

  1. Fetch in each page's getStaticProps/getServerSideProps: This keeps static optimization working. You can create a helper function to fetch config and reuse it across pages:
// lib/sanity.ts
export async function getConfig() {
  return client.fetch(siteConfigQuery);
}

// pages/index.tsx
export async function getStaticProps() {
  const config = await getConfig();
  return { props: { config } };
}
  1. Use React Context with initial data: Fetch config in individual pages and pass it through context to your layout/navigation components.

  2. For Next.js 13+ App Router: Use React Server Components to fetch config in your root layout, which is much cleaner and doesn't have the same trade-offs.

Since you're on Next.js 10, if you need the config globally and the SSR trade-off is acceptable for your use case, the getInitialProps approach will work. Just be aware that it means every page will be server-rendered on each request rather than being statically generated at build time.

Show original thread
6 replies
Your component return doesn’t wait for pageProps to populate and since you’re assigning to a prop field it doesn’t cause a rerender after it’s fetched. Prop changes only cause a rerender if it comes from the parent component.
You’d need to place it in something like a
useEffect
hook and state variable for it to update the component.
Thanks, 🤦. Of course. Do you know when you stare are something for hours and it’s obvious!!!!
Hahaha classic! 😂
It can be the most annoying thing, but you become blind to your own bug sometimes
totally. works now in 2 mins!!! Was looking for hours last night!
useEffect(() => {
        // Add site config from sanity
        const res = client.fetch(siteConfigQuery).then((config) => {
            setPropState((previous) => ({
            ...previous,
            config,
        }));
            return ;
        });
        
    }, []);

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?