Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Custom Polyfills for App Router is outdated #74730

Open
osdiab opened this issue Jan 10, 2025 · 2 comments
Open

Docs: Custom Polyfills for App Router is outdated #74730

osdiab opened this issue Jan 10, 2025 · 2 comments

Comments

@osdiab
Copy link

osdiab commented Jan 10, 2025

What is the documentation issue?

In the Custom Polyfills section of the Supported Browsers page for App Router, it still tells you to use a custom <App /> component, but that isn't a thing for the app router.

https://nextjs.org/docs/architecture/supported-browsers#custom-polyfills

Is there any context that might help us understand?

This discussion exists, but it was marked as complete when a solution was posted for the Pages router. Now that the App Router is a thing it's not very clear how you're supposed to set up polyfills without needing to sprinkle polyfill logic in every individual component that needs it, like those of @formatjs for instance.

Does the docs page already exist? Please link to it.

https://nextjs.org/docs/architecture/supported-browsers#custom-polyfills

@osdiab osdiab added the Documentation Related to Next.js' official documentation. label Jan 10, 2025
@osdiab
Copy link
Author

osdiab commented Jan 10, 2025

Based on other comments there I came up with this approach to conditionally load the @formatjs/intl-durationformat polyfill as an example, but it is flawed; some open questions:

  • this messes up SSR by now refusing to actually render anything if it's on the server; because of that I am not sure how to conditionally apply polyfills. If I don't hide the content before the polyfills load I worry that the polyfills may not be loaded by the time the React render occurs and may crash. How can this be done cleanly?
  • does this work even for deeply nested components that I presume may not be rendered together with the root layout? My understanding is that RSC can render parts of the component tree independently so I do not actually know if loading polyfills in a root layout will always work.
  • is there a more natural way to do this that does not involve so much boilerplate?
// app/load-polyfills.ts
// this should not have a "use client" or "use server" directive so that it can be included both on the server and client

import { shouldPolyfill } from "@formatjs/intl-durationformat/should-polyfill";
// if you always want to load a polyfill, feel free to add logic here

async function loadIntlDurationFormatPolyfill() {
  const unsupported = shouldPolyfill();
  if (!unsupported) {
    return;
  }

  await import("@formatjs/intl-durationformat/polyfill-force");
}

export async function loadPolyfills() {
  await loadIntlDurationFormatPolyfill();
  // any other polyfills you want to load asynchronously
}
// app/polyfill-provider.tsx
// this file is a client component, so by loading the polyfills
// here it will be loaded on the client.
"use client";
import { loadPolyfills } from "./load-polyfills";

const loadPolyfillsPromise = loadPolyfills();

export async function PolyfillProvider({children}) {
  const [polyfillsLoaded, setPolyfillsLoaded] = useState(false);

  useEffect(() => {
    async function waitForPolyfills() {
      await loadPolyfillsPromise;
      setPolyfillsLoaded(true);
    }
    waitForPolyfills();
  }, []);

  // don't render anything until the polyfills are loaded to avoid race conditions
  return polyfillsLoaded ? <>{children}</> : null;
}
// app/layout.tsx
// This is a server component, it has no "use client" at the top,
// so this will load on the server.
import { loadPolyfills } from "./load-polyfills";
import { PolyfillProvider } from "./polyfill-provider";

const loadPolyfillsPromise = loadPolyfills();

export default async function RootLayout({children}) {
  // ...
  await loadPolyfillsPromise;
  return (
    <PolyfillProvider>
      {children}
    </PolyfillProvider>
  );
}

@markedwards
Copy link

Yes, what is the right way to load additional core-js polyfills, for example. This works, but is it wrong?

"use client";

import "core-js/features/iterator";

export default function CustomPolyfills() {
  return null;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants