The final code for the project is here, and if you prefer a video walkthrough it's available on my YouTube channel here:
The Quest For Beats
I needed to use an external component for a Next.JS project I'm working on: mixmotion-player which is a React front-end for assets stored on Mixcloud.
Mixmotion is a client-side only react component, meaning it has some dependencies that make it tricky to integrate with something that does server side rendering like Next. It will depend on globals like window which don't exist on the server.
Here's how I got it to work, and necessary steps which you will probably have to do if you're integrating a similar React component.
1) I was unfamiliar with the component, so I created a new blank React project running on Vite.
2) I imported Mixmotion into that project using npm install mixmotion-player.
3) I used boilerplate code from the Mixmotion GitHub page, and was soon listening to sick beats in my browser.
Webpack Kills The Vibe
Things were going well! Then I tried integrating it with my existing Next project and ran into issues.
First, an easily fixable one: Mixmotion prefers React 18.2.0 so I edited package.json to use those React versions.
Then the hard one: Mixmotion not finding the window global on server-side renders.
There are a couple different ways to deal with this, I've found using dynamic imports to be the cleanest.
I created a dynamic wrapper for the Mixmotion component:
import dynamic from "next/dynamic";
const MixmotionPlayer = dynamic(() => import("mixmotion-player").then((mod) => mod.MixmotionPlayer), {
ssr: false,
});
export default MixmotionPlayer;
We're following a fairly standard flow here:
-Importing dynamic from a Next module
-Creating a container via MixmotionPlayer for our component
-Calling the dynamic function, which in turn imports a named export (mod stands for module here)
-Passing an option of ssr: false which tells Next that this should not be included in server side rendering.
We're all set, right? Not quite yet.
We've dealt with runtime issues, but we have to keep in mind that by default Next uses webpack to create the production bundle. This will work with next dev, but will throw an error when using next build about webpack being unable to resolve the module.
When you deploy to a service like Vercel, you'll be taking the bundle generated by next build and not using next dev, so this is an issue. We can fix it by telling Next not to include this module in the bundle.
You'll need to change your next.config.ts or .js to something like the following (this example uses TypeScript):
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
reactStrictMode: true,
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
'mixmotion-player': false,
};
}
return config;
}
};
export default nextConfig;
Webpack is checking if we're doing a server or client side build, and if it's a client build we're telling Webpack to not try to resolve the mixmotion-player package.
It might seem odd that we're excluding the client side! After all, how can the client work if this package is excluded?
It's only being excluded from Next's static analysis at build time, not from the project entirely. It will still be dynamically loaded by the client at runtime.
Now that next build works, I was able to deploy the project to Vercel via vercel --prod. And voila: Sick beats on Next via mixmotion-player!