shopify-nextjs-toolbox
                                
                                 shopify-nextjs-toolbox copied to clipboard
                                
                                    shopify-nextjs-toolbox copied to clipboard
                            
                            
                            
                        No AppBridge context provided.
Thank you for your amazing work, I couldn't do my own API routing with Shopify's CLI server.
I tried using in home.js:
import { useAppBridge } from "@shopify/app-bridge-react";
const app = useAppBridge();
But I get the error:
Error: No AppBridge context provided. Your component must be wrapped in the <Provider> component from App Bridge React.
ShopifyAppBridgeProvider should do the job looking at the source code. Maybe I just did something wrong.
I ended up rolling back to my old _app.js.
import React from "react";
import "../styles/globals.css";
import enTranslations from "@shopify/polaris/locales/en.json";
import { AppProvider } from "@shopify/polaris";
import { Provider } from "@shopify/app-bridge-react";
import App from "next/app";
class MyApp extends App {
    render() {
        const { Component, pageProps, host } = this.props;
        return (
            <AppProvider i18n={enTranslations}>
                <Provider
                    config={{
                        apiKey: process.env.NEXT_PUBLIC_SHOPIFY_API_PUBLIC_KEY,
                        host: host,
                        forceRedirect: true,
                    }}
                >
                    <Component {...pageProps} />
                </Provider>
            </AppProvider>
        );
    }
}
MyApp.getInitialProps = async ({ ctx }) => {
    return {
        host: ctx.query.host,
    };
};
export default MyApp;
And things are working, I guess sometimes the ShopifyAppBridgeProvider doesn't return a Provider.
Hmm that is really strange, I have not seen this issue myself yet.
Which version of Next/React, Polaris & AppBridge are you using?
From package.json:
"@shopify/app-bridge": "^2.0.3",
"@shopify/app-bridge-react": "^2.0.11",
"@shopify/app-bridge-utils": "^2.0.3",
"@shopify/polaris": "^6.6.0",
"next": "10.0.0",
I think I updated the version after I ran into the problem with the original example code. I will do more diagnosing when I get some time, I guess it is the following: in ShopifyAppBridgeProvider:
  if (
    typeof window == "undefined" ||
    !window.location ||
    !shopOrigin ||
    !host
  ) {
    return <Component {...pageProps} />;
  }
Maybe it is working as intended and I should have a try statement when I call useAppBridge instead!
For me, I was getting an error that no i18n was provided. I put the ShopifyAppBridgeProvider within the AppProvider and it worked again:
<AppProvider i18n={enTranslations}> <ShopifyAppBridgeProvider Component={Component} pageProps={pageProps}> <Component {...pageProps} /> </ShopifyAppBridgeProvider> </AppProvider>
I've run into a similar issue using https://github.com/ctrlaltdylan/shopify-session-tokens-nextjs as the template for the site and _app.js unchanged (i.e. using <ShopifyAppBridgeProvider>)
It appears that the page being loaded flashes briefly without the App bridge loaded, causing any app bridge dependent components to throw an error such as the one above: Error: No AppBridge context provided. Your component must be wrapped in the <Provider> component from App Bridge React. A workaround for this is to put any components that use the app bridge inside a dummy components that only renders them when the app bridge is available. E.g:
const ConditionalRender = ({ children }) => {
  const shopOrigin = useShopOrigin();
  const host = useHost();
  // Don't render AppBridge components until the AppBridge is available.
  if(typeof window == "undefined" ||
  !window.location ||
  !shopOrigin ||
  !host) return null
  return children;
}
It's my understanding that:
if (
    typeof window == "undefined" ||
    !window.location ||
    !shopOrigin ||
    !host
  ) {
    return <Component {...pageProps} />;
  }
Inside ShopifyAppBridgeProvider is designed so that NextJS does not render the provider server side? I'm not super familiar with how SSR works, but I would suspect that is the likely culprit. My guess would be it is trying to render the AppBridge components server side, which somehow breaks it?
A bit of an aside but another issue I ran into is shopify's confusing naming system of their libraries:
- @shopify/app-bridge-react- react components you can use like- <Titlebar />- this requires a provider up the component tree to provide the app - done by- <ShopifyAppBridgeProvider>
- @shopify/app-bridge/actions- functions you use like- TitleBar.create(app, titleBarOptions);- you can get the app from- useAppBridge();which will get the app from the provider further up the component tree. I will admit I spent far too long stuck on this 😅
I'm having the same issue with <ShopifyAppBridgeProvider> with nextjs. Going to try debug for another hour or so otherwise might end up rolling back to Shopify's App Bridge directly.
This seems to work for me.
import "@shopify/polaris/build/esm/styles.css";
import App from "next/app";
import type { AppProps } from "next/app";
import { ShopifyAppBridgeProvider } from "shopify-nextjs-toolbox";
import translations from "@shopify/polaris/locales/en.json";
import { AppProvider } from "@shopify/polaris";
function MyApp({ Component, pageProps }: AppProps) {
  return (
    <AppProvider i18n={translations}>
      <ShopifyAppBridgeProvider Component={Component} pageProps={pageProps}>
        <Component {...pageProps} />
      </ShopifyAppBridgeProvider>
    </AppProvider>
  );
}
MyApp.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext);
  return { ...appProps }
}
export default MyApp;
As per Next.js documentation by using getInitialProps it will will disable Automatic Static Optimization. I don't think these optimisations really matter for Shopify Apps.
Did anyone found a better solution to this? On my codebase it appears that the issue triggers anytime I try to use Polaris components to build out the pages generated by NextJS.
So if I replace home.js from the example from this:
import React, { useEffect, useState } from "react";
import { useApi, useShopOrigin } from 'shopify-nextjs-toolbox';
export default function Home() {
  const shopName = useShopOrigin();
  const api = useApi();
  const [response, setResponse] = useState(false);
  // the session token is now available for use to make authenticated requests to the our server
  useEffect(() => {
    api.get("/api/verify-token")
    .then((res) => {
      setResponse(res.data);
    })
    .catch((res) => {
      console.log(res);
    });
  }, []);
  return (
    <div className="container">
      <div className="card">
        <h2>Current Decoded Session Token</h2>
        <p>
          This is the decoded session token that was sent to the server after the OAuth handshake finished.
        </p>
        <p>
          You can use the backend middleware <code>withSessionToken</code> to verify the API request came from the currently logged in shop 
        </p>
        <p>
          Wrap your API route with <code>withSessionToken</code> to access the shop's origin (a.k.a the shop's name in <code>shop-name.myshopify.com</code> format) in the backend.
        </p>
        <pre>
          {JSON.stringify(response, null, 4)}
        </pre>
      </div>
      <div className="card">
        <h2>Currently logged in as</h2>
        <code>{shopName}</code>
      </div>
    </div>
  )
}
To this:
import React, { useEffect, useState } from 'react';
import { useApi, useShopOrigin } from 'shopify-nextjs-toolbox';
import { Card, EmptyState, Page, Layout, TextContainer, Image, Stack, Link, Heading, Loading, SkeletonBodyText } from '@shopify/polaris';
import { TitleBar, useNavigate } from '@shopify/app-bridge-react';
export default function Home() {
  const shopName = useShopOrigin();
  const api = useApi();
  const [response, setResponse] = useState(false);
  // the session token is now available for use to make authenticated requests to the our server
  useEffect(() => {
    api
      .get('/api/verify-token')
      .then((res) => {
        setResponse(res.data);
      })
      .catch((res) => {
        console.log(res);
      });
  }, []);
  return (
    <Page>
      <TitleBar title="Home" />
      <Layout>
        <Layout.Section>
          <Card>
            <Card.Section>
              <div className="card">
                <h2>Current Decoded Session Token!</h2>
                <p>This is the decoded session token that was sent to the server after the OAuth handshake finished.</p>
                <p>
                  You can use the backend middleware <code>withSessionToken</code> to verify the API request came from the currently logged
                  in shop
                </p>
                <p>
                  Wrap your API route with <code>withSessionToken</code> to access the shop's origin (a.k.a the shop's name in{' '}
                  <code>shop-name.myshopify.com</code> format) in the backend.
                </p>
                <pre>{JSON.stringify(response, null, 4)}</pre>
              </div>
              <div className="card">
                <h2>Currently logged in as</h2>
                <code>{shopName}</code>
              </div>
            </Card.Section>
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}
the issue immediately starts. I wonder if this has something to do with how NextJS generates pages, possibly compiling the page in a way that is incompatible with Polaris.
Disabling Automatic Static Optimization as mentioned by @Jore unfortunately doesn't fix this. Is shopify-nextjs-toolbox fundamentally incompatible with Polaris for the application pages? Is that why @ctrlaltdylan created his own components for the landing page rather than using the ones provided by Polaris?