gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
32.13k stars 2.4k forks source link

Gradio JS client does not work within NextJS #8864

Open tlaanemaa opened 1 month ago

tlaanemaa commented 1 month ago

Describe the bug

The Gradio JS client does not work with NextJS, and likely any other similar framework, due to how it detects it's runtime.

This manifests in the following error when running a dev environment npm run dev with NextJS.

./node_modules/@gradio/client/dist/index.js:878:0
Module not found: Can't resolve 'fs/promises'

https://nextjs.org/docs/messages/module-not-found

The problem seems to be that NextJs seems to run client side code with node versions set, so Gradio thinks it's in a NodeJS environment and goes ahead and requires node dependencie, which fails. The relevant code lines can be seen here and here.

It would be nice to have a way to manually set Gradio runtime environment, perhaps through an env variable or something like that because even tho Im using server side rendering and all that, in my case I always want the gradio client to run as if it was in the client.

Have you searched existing issues? 🔎

Reproduction

I dont have a simple way to reproduce this, but I think if you just set up a fresh NextJS app, install @gradio/client, and then try to use it in dev mode then the bug should manifest easily.

Screenshot

No response

Logs

No response

System Info

I am running on Windows and using the @gradio/client inside a pre-existing NextJS application

Severity

Blocking usage of gradio

alex-radulescu-b1 commented 1 month ago

Experiencing the exact same issue. Any temporary suggestions at least?

tlaanemaa commented 1 month ago

My current workaround is to load the CDN hosted version with this hacky helper:

export const isNode = typeof document === "undefined";

// Super hacky way to load ES modules in the browser
// This is needed to work around this issue: https://github.com/gradio-app/gradio/issues/8864
export const loadESModule = <T = any>(url: string): Promise<T> =>
  new Promise((resolve, reject) => {
    if (isNode) {
      console.log("Skipping module load in Node.js");
      resolve({} as any);
      return;
    }

    const globalName = "___module_resolve" + Date.now() + Math.random();
    (window[globalName as any] as any) = resolve;
    const script = document.createElement("script");
    script.type = "module";
    script.textContent = `
          import * as module_content from '${url}';
          const resolve = window['${globalName}'];
          delete window['${globalName}'];
          resolve(module_content);
        `;
    script.onerror = (err) => reject(err);

    document.head.appendChild(script);
  });

The NodeJS checks are in place to get around NextJS doing server side rendering. I want my app to be server side rendered in general, but this hack doesn't work in the server.

In general, I think the issue stems from the fact that the client library is an ES module and must be executed in module context, but I guess NextJS isn't doing that. Related to that, the readme on the library appears to be wrong in this line:

<script src="https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"></script>

The above line doesn't work and throws an error when added to your HTML file. The reason is the same that the library is an ES module, so the script type needs to be set to module for this to work, as described here. So the correct snippet would be:

<script type="module" src="https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"></script>

I would say that for the client library this is currently definitely a blocking issue as it makes the library effectively unusable, outside explicit ES modules, without some crazy workarounds like the one I shared above.

hannahblair commented 1 month ago

Related to #7693