How to use Babylon with Next JS

Hi im new to the forum. Could someone help me with a step by step guide on how to use Babylon with Nextjs. I just need a simple example/tutorial beginning with what to install and how to write the code into a Nextjs index.js file so i can render it on the browser. Thanks to everyone in advance


No sure we have a tuto for it but I think @RaananW is pretty familiar with Next and might have pointers

I’ve used Babylon with NextJS. This is an example component I’ve used:

import { Engine, Scene } from "babylonjs";
import React, { useEffect, useRef, useState } from "react";

const BabylonScene = (props) => {
  const reactCanvas = useRef(null);
  const {
  } = props;

  const [loaded, setLoaded] = useState(false);
  const [scene, setScene] = useState(null);

  useEffect(() => {
    if (window) {
      const resize = () => {
        if (scene) {
      window.addEventListener("resize", resize);

      return () => {
        window.removeEventListener("resize", resize);
  }, [scene]);

  useEffect(() => {
    if (!loaded) {
      const engine = new Engine(
      const scene = new Scene(engine, sceneOptions);
      if (scene.isReady()) {
      } else {
        scene.onReadyObservable.addOnce((scene) => props.onSceneReady(scene));

      engine.runRenderLoop(() => {
        if (typeof onRender === "function") {

    return () => {
      if (scene !== null) {
  }, [reactCanvas]);

  return (
      style={{ width: "100%", height: "100%" }}

export default BabylonScene;

Here is how I used it with a custom imported model:

import React from "react";
import { SceneLoader } from "babylonjs";
import "babylonjs-loaders";
import BabylonScene from "./BabylonScene";

const onSceneReady = (scene) => {
    "path to 3d model/",
    (meshes) => {
      scene.activeCamera = meshes.cameras[1];

  return scene;

const onRender = (scene) => {};

const CafeBanner = () => {
  return (

export default CafeBanner;

I believe when I created this I was following this in the documentation: Babylon.js and React | Babylon.js Documentation

1 Like

The code @jgonzosan offered seems like a perfect starting point.
As Next.js is technically a static react generator, you might also be able to use @brianzinn’s babylon-react component, but I haven’t tried it yet, so I can’t offer any advice on that.

I updated my original response as it was only half of how I used it. The BabylonScene component is essentially a “template” that can be used as the basis for a more customized component. The code I posted includes a 3d scene of a cafe I imported.

hi @John_Carlisle (and thanks @RaananW for the mention).

I have a sample repo here:

If you don’t want to use the react renderer and prefer imperative as posted above then have a look at the babylonjs-hook branch. You are going to have better tree-shaking results right now until I get a v4 out on react-babylonjs with dynamic registration.

Definitely have a look at the next.config.js require on next-transpile-modules - you can actually remove the withTM parth completely and go like this:

import dynamic from 'next/dynamic'

then in page/index.tsx change

import Wave from '../components/elements/Wave/Wave'


const Wave = dynamic(() => import('../components/elements/Wave/Wave'), {
  ssr: false

That repo is a year old, but would appreciate if you have any updates and would accept any PRs to bring up-to-date.

The bundle analyzer is on for that project - have a look at the output if you want to optimize the output. Make sure all of your imports are explicit to file. ie: don’t import from @babylonjs/core, but @babylonjs/core/fullPathHere



With Next.js you should only dynamically import Babylon.js because some packages may cause error when loaded in Node.js.

Next.js uses Node.js on the server side, it will load your code twice, in Node.js first, and then in browser, but the code will only run in browser.

Even if you do not have packages that may break in Node.js, it will still be slower to serve the static pages, than if you skipped loading Babylon from backend.

Here is what I use for any React component that imports anything from Babylon:

Next.js page file

/* next/pages/index.js */
import { ClientOnly, dynamicImportOptions } from 'client/utils/next'
import dynamic from 'next/dynamic'

// This is the component that imports something from Babylon.js
const BabylonComponent= dynamic(() => import(`../BabylonComponent`), dynamicImportOptions)

export default function HomePage (props) {
  return <ClientOnly {...props}>{BabylonComponent}</ClientOnly>

Utility file

/* client/utils/next */
import React, { useEffect, useState } from 'react'
import Loading from 'Loading' // Your custom Loading placeholder component (i.e. show spinner)

// @Note: next.js dynamic import only works for React Components,
// and only starts loading when the Component gets called
export const dynamicImportOptions = {ssr: false, loading: () => <Loading loading/>}

 * HOC Wrapper to Render given React Function Component on client side only
export function ClientOnly ({children: Component, ...props}) {
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true))
  return mounted && <Component {...props}/>

I might have some time later to create github template (right now stapped for time), if people want this.

1 Like

Hi everyone, i really appreciate all the replies. Ive had a go at using all the suggested ways to solve it (just seem to be having some challenges with the transpile issue; not being able to resolve or import babylon-loaders and recieving errors for next/dynamic options/requiring object literal). If anyone comes across a really basic step by step way to do it that is guarenteed to work(including what to install, preferrably no need to modify config files, and then exactly what code to paste in each nextjs page), that would be so greatly appreciated. Thanks again for all the help so far

That means Next.js tries to import Babylon in Node.js environment, or you have errors in your setup not related to Babylon.

My code above was copy pasted from existing working project, however, right now I do not have time to setup a template repo to share with you.

You can try my next.config.js:

// @Note: next.config.js gets loaded once at the start, then babel.config.js (loaded repeatedly)
const {withPlugins, optional} = require('next-compose-plugins')
const {PHASE_PRODUCTION_SERVER} = require('next/constants')
let {modulesToTranspile} = require('./config')
modulesToTranspile = modulesToTranspile.filter(v => v !== '@babylonjs')
const clientEnvs = {}
for (const key in process.env) {
  if (key.indexOf('REACT_APP_') === 0) clientEnvs[key] = process.env[key]
/** Tested with Next.js v12.22.1 Webpack v4.4.1 */
module.exports = withPlugins(
  /** IMPORTANT: the order of plugins matter! */
    /* Must be the first plugin (to work with decorator {legacy: true}) */
    [optional(() => require('@next/bundle-analyzer')({
      enabled: process.env.ANALYZE === 'true',
    })({})), {}, ['!', PHASE_PRODUCTION_SERVER]],

    /* Webpack configuration must go here */
    [optional(() => require('next-transpile-modules')(modulesToTranspile)), {
       * The webpack function is executed twice, once for the server and once for the client.
       * @see
      webpack: (config, {buildId, dev, isServer, defaultLoaders, webpack}) => {
        // Comment this out if you do not use GraphQL
          test: /\.(graphql|gql)$/,
          loader: 'graphql-tag/loader', // works with fragment #import
        return config

  /** next.config.js configuration */
    // @note: 'next-compose-plugins' has a bug and does not call webpack(config) here
    // @see:
     * For Security reasons, Next.js does not export process.env to client side.
     * Manually export all envs starting with `REACT_APP_*` to sync with CRA best practices
    publicRuntimeConfig: {
      NODE_ENV: process.env.NODE_ENV,
     * @see:
    i18n: {
      locales: ['en', 'fr', 'ru'],
      defaultLocale: 'en',
     * @see:
    async rewrites () {
      return {
        // These rewrites are checked after both pages/public files
        // and dynamic routes are checked
        fallback: [
          { // Rewrite everything else to use `pages/spa.js` Single Page Application
            source: '/:path*',
            destination: '/spa',


const {modulesToTranspile} = require('./config')
module.exports = (api) => {
  api.cache(false) // set cache as true/false

  return {
    plugins: [
          'legacy': true
    presets: [
    ignore: [
      // this duplicated declaration from next.config.js produces slightly smaller bundle
      new RegExp(`node_modules/(?!(${modulesToTranspile.join('|')})/)`)


module.exports = {
  modulesToTranspile: [
    'validator', // importing ES6 code for tree shaking
     * frontend only
     * next@10.2.3 with next-transpile-modules@7.3.0 must have this disabled
     * use dynamic import instead to avoid rendering Babylon server side and slowing down dev
    '@babylonjs', // required for Jest test


  "dependencies": {
    "next": "10.2.3"
  "peerDependencies": {
    "react": "x",
  "devDependencies": {
    "@babel/plugin-proposal-decorators": "^7.10.1",
    "@next/bundle-analyzer": "10.2.3",
    "next-compose-plugins": "^2.2.1",
    "next-transpile-modules": "7.3.0",
    "@testing-library/jest-dom": "^5.11.0",
    "@testing-library/react": "^10.4.3",
    "babel-jest": "^26.1.0",
    "babel-plugin-import-graphql": "^2.8.1",
    "babel-plugin-lodash": "^3.3.4",
    "concurrently": "^5.2.0",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^26.1.0",
    "jest-transform-graphql": "^2.1.0",
    "madge": "^3.9.2",
    "react-test-renderer": "^16.13.1",
    "rimraf": "^3.0.2"

Just my two cents here, and not sure it will help in any way, but don’t use babylonjs-loaders, instead use the es version at @babylonjs/loaders . This might work a little better.

For nextjs v11 and below

because next is a cjs node server. If you use the esm version of babylon, you will just have to transpile it to cjs. Although, i think they added esm support in the latest version. They also added swc as the default compiler but will revert to babel if u have a babel config in your project.

The core issue is that next skips transpiling node modules, because practically all packages ship cjs and esm or umd. For packages that dont, like babylon, most people use the “next-transpile-modules” plugin which just tells next to transpile certain packages inside of node modules.

Here is an example
.next-babylonjs/next.config.js at master · Flux159/next-babylonjs · GitHub

@RaananW I was wrong, updated correction:

For nextjs v12 and above

I just confirmed with the example repo above you can remove next-transpile-modules with nextjs v12, using either babel or swc. So you can use @babylon/core and babel-plugin-glsl (glslify in a babel macro) . w00t.