Getting started with Three-js in Next-js 13

April 26, 2023

(1y ago)

Hello There 🐼

Let's make a cool 3D webpage using Three-js in Next.js 13.


Start off with creating a new Next-js project with app-dir and install three-js. Here i am using JS, tailwindCSS and Next-js 13.2 with app-directory.

terminal
npx create-next-app@latest
npm i three

Next steps

  1. Navigate to /app/page.js and clean up the starter code.
  2. Make a Three.js component file in /app director y (/app/Three.js).
  3. Three.js is the file where we will be writing our three-js code.

populate /app/Three.js with the following starter code.

/app/Three.js
export default function Three() {
  return <>Three-js code goes here</>
}

go ahead and import this component in /app/page.js.

/app/page.js
import Three from './Three'
 
export default function Home() {
  return <Three />
}

Note: In the next following steps you will be editing /app/Three.js file.


Next-steps

  1. Creating a scene in Three-js.
  2. Adding some 3D geometry to the webpage.

You can think of your 3D scene as a movie shooting session. Where you are required to have a camera, a scene and film in the camera to capture the movie. Similarly here in Three-js you need a camera, a scene and a renderer. We need to create these three things.

populate /app/Three.js page with the following code that sets up our scene in three-js.

/app/Three.js
'use client'
import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'
 
export default function Three() {
  const sceneRef = useRef(null)
  const cameraRef = useRef(null)
  const rendererRef = useRef(null)
 
  useEffect(() => {
    sceneRef.current = new THREE.Scene()
    cameraRef.current = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    )
    rendererRef.current = new THREE.WebGLRenderer({ antialias: true })
  }, [])
  return <>Three-js cool.</>
}

Next-steps

  1. Using the scene that was created to render something on the screen.
  2. Making a SphereGeometry.
  3. Rendering the SphereGeometry on the page.

In the following code block you are rendering your 3D geometry as soon as you load the page. This is achieved by useEffect() hook. In this hook call you are setting the renderer's size viz, window.size and pushing the 3D object to the end of the page.

/app/Three.js
'use client'
import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'
 
export default function Three() {
  const sceneRef = useRef(null)
  const cameraRef = useRef(null)
  const rendererRef = useRef(null)
 
  useEffect(() => {
    sceneRef.current = new THREE.Scene()
    cameraRef.current = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    )
    rendererRef.current = new THREE.WebGLRenderer({ antialias: true })
  }, [])
 
  // New Code Here
  useEffect(() => {
    const scene = sceneRef.current
    const camera = cameraRef.current
    const renderer = rendererRef.current
 
    renderer.setSize(window.innerWidth, window.innerHeight)
 
    document.body.after(renderer.domElement)
  }, [])
  return <></>
}

Making the SphereGeometry

The simplest geometry in three-js is a ball i.e; SphereGeometry.

Sphere geometry takes a bunch of parameters in it's constructor while creating it. Explore around the documentation here to figure out how you can customize your Ball.

The following code creates a Ball and adds it to the scene.

/app/Three.js
const geometry = new THREE.SphereGeometry(15, 32, 16)
const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })
const sphere = new THREE.Mesh(geometry, material)
scene.add(sphere)

Now for the final step you can copy the following code block to your /app/Three.js file and run npm run dev.

/app/Three.js
'use client'
import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'
 
export default function Three() {
  const sceneRef = useRef(null)
  const cameraRef = useRef(null)
  const rendererRef = useRef(null)
 
  useEffect(() => {
    sceneRef.current = new THREE.Scene()
    cameraRef.current = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    )
    rendererRef.current = new THREE.WebGLRenderer({ antialias: true })
  }, [])
 
  useEffect(() => {
    const scene = sceneRef.current
    const camera = cameraRef.current
    const renderer = rendererRef.current
 
    renderer.setSize(window.innerWidth, window.innerHeight)
 
    document.body.after(renderer.domElement)
 
    // New Code Here
    const geometry = new THREE.SphereGeometry(15, 32, 16)
    const material = new THREE.MeshBasicMaterial({ color: 0xffff00 })
    const ball = new THREE.Mesh(geometry, material)
    scene.add(ball)
 
    const animate = () => {
      requestAnimationFrame(animate)
      renderer.render(scene, camera)
    }
 
    animate()
  }, [])
 
  return <></>
}

And That's It Folks now you have a ball on your screen.


Fun-Note

you can wrap your ball with any picture to make it look cool. Or make it look like a planet or a star or anything that comes to your imagination. Let's make a moon 🌚,

you can get the 🌚 jpg from here.

/api/Three.js
'use client' import React, {(useRef, useEffect)} from
'react' import * as THREE from 'three'
 
export default function Three() {
  const sceneRef = useRef(null)
  const cameraRef = useRef(null)
  const rendererRef = useRef(null)
 
useEffect(() => {
    sceneRef.current = new THREE.Scene()
    cameraRef.current = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    )
    rendererRef.current = new THREE.WebGLRenderer({ antialias: true })
  }, [])
 
useEffect(() => {
const scene = sceneRef.current
const camera = cameraRef.current
const renderer = rendererRef.current
 
    renderer.setSize(window.innerWidth, window.innerHeight)
 
    document.body.after(renderer.domElement)
 
    // New Code Here
    const moonImg = 'https://tinyurl.com/spj4rujm'
 
    const ball = new THREE.Mesh(
      new THREE.SphereGeometry(10, 150, 20),
      new THREE.MeshBasicMaterial({
        map: new THREE.TextureLoader().load(moonImg),
      })
    )
 
    scene.add(ball)
 
    const animate = () => {
      requestAnimationFrame(animate)
 
      // Rotating the Moon around it's own axis
      ball.rotation.x += 0.01
 
      renderer.render(scene, camera)
    }
 
    animate()
 
}, [])
 
return <></>
}
 

That's it For Now folks. Maybe in not so far future i might make a something like npx create-next-three.



Good Day 🐼