next.jsuppy

Uppy nextjs "document is not defined", how to make it work?


I'm trying to make uppy work in nextjs. Nextjs tries to render every page on the server first and will also evaluate and create bundles on the server. Uppy uses native browser APIs such as document and window which aren't defined on the server.

Is there a way to make it work regardless?

Here's a sandbox illustrating the problem: https://stackblitz.com/edit/stackblitz-starters-kjtfxx?file=app%2Fpage.tsx


Solution

  • Since Next.js will try to pre-render components in the server, you do not have access to browser objects such as window or document. Anything that requires those objects should run inside a useEffect().

    How about something like this?

    'use client';
    import { useEffect, useRef } from 'react';
    import Uppy from '@uppy/core';
    import Dashboard from '@uppy/dashboard';
    
    import '@uppy/core/dist/style.css';
    import '@uppy/dashboard/dist/style.css';
    
    export default function Page() {
      const uppy = useRef<Uppy>(new Uppy({}));
    
      useEffect(() => {
        try{
          uppy.current.use(Dashboard, {
            inline: false,
            trigger: '#trigger',
            showProgressDetails: true,
            proudlyDisplayPoweredByUppy: true,
          });
        } catch (error) {
          if (!(error as Error).message.includes('Already found a plugin')) {
            throw error;
          }
        }
      }, []);
    
      return <button id="trigger">Trigger</button>;
    }
    

    We save the Uppy instance inside a ref since we need to keep the same instance across re-renders. We can then attach the Dashboard plugin inside a useEffect.


    As a side note, you may notice a strange try/catch, when using React's strict mode useEffect() runs twice.

    I couldn't find a way to remove the plugin from the Uppy instance (since we could use this inside a cleanup function), so the next bet would be to check if the plugin had already been registered. I also couldn't find a way to check if plugins are already registered so the next bet would be to check if re-registering the same plugin would throw an error.