DEV Community

Cover image for 3D Product Card With ReactJS
Mohamed Soliman
Mohamed Soliman

Posted on • Edited on

3D Product Card With ReactJS

In this article, we will create a 3D model of a product and render it inside of a product card. By the end of this article, you will be able to render 3D models in your website.

Download a 3D model

First, You will need to get a 3D model either by creating it by using 3D apps, or by downloading them from the internet. I have chosen the easy way and downloaded them for free from open3dmodel.com. Make sure it has glb file extension. If it doesn't have glb file extension we can use Blender to export it as glb.

glb is a binary container format of gltf which makes it more lightweight in the terms of size than gltf and other 3D model extensions. That's make it easier to load and render in websites.

I have downloaded a model of converse shoe that looks like that:
3D Shoe Model

Setting up the environment

First, we need to create a new React application.

npx create-react-app 3d-product
Enter fullscreen mode Exit fullscreen mode

Then, we need to install the dependencies that we will use in this project. We will use Three.js library, @react-three/fiber and @react-three/drei.

we can install them with npm

npm install three @react-three/fiber @react-three/drei
Enter fullscreen mode Exit fullscreen mode

or by using yarn

yarn install three @react-three/fiber @react-three/drei
Enter fullscreen mode Exit fullscreen mode

Three.js is used to make a 3D scene, and @react-three/fiber is used to re-render models in the scene. While @react-three/drei is a collection of useful helpers and fully functional, ready-made abstractions for @react-three/fiber

Making a React component of the model

After installing the dependencies, we will need to covert the glb file into a reusable react component. we can easily do that by just running this command in the directory that contains the glb file:

npx gltfjsx model.glb
Enter fullscreen mode Exit fullscreen mode

After running this command, new JavaScript file will be generated. It would be something like that:

import React, { useRef } from 'react'
import { useGLTF } from '@react-three/drei'

export function Model(props) {
  const { nodes, materials } = useGLTF('/model.glb')
  return (
    <group {...props} dispose={null}>
      <group position={[0.67, -1.61, 3]} rotation={[Math.PI / 2, 0, 0]} scale={0.1}>
        <group position={[-37.27, -6.54, 0.15]} rotation={[1.57, -0.02, 3.13]}>
          <group position={[-25.76, -1.15, 0]}>
            <mesh geometry={nodes.Outsole_1.geometry} material={materials['706a6a']} position={[-0.19, 3.95, -5.76]} rotation={[0, 0, -3.14]} scale={[-0.98, -0.89, -0.98]} />
            <mesh geometry={nodes.Plate.geometry} material={materials.Physical10} />
            <mesh geometry={nodes.Toe.geometry} material={nodes.Toe.material} />
            <mesh geometry={nodes._polySurface204.geometry} material={nodes._polySurface204.material} />
            <mesh geometry={nodes._polySurface205.geometry} material={materials.fef7f7} position={[40.76, 12.34, -22.15]} />
            <mesh geometry={nodes.Above.geometry} material={materials.Standard5} />
            <mesh geometry={nodes.Below.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Stripe.geometry} material={materials.Standard6} />
          </group>
          <group position={[-25.76, -1.15, 0]}>
            <group position={[-20.21, 47.7, -0.77]}>
              <mesh geometry={nodes.Tongue_2.geometry} material={materials.Physical9} position={[20.21, -48.47, 0.65]} />
              <mesh geometry={nodes.Tongue_Binding.geometry} material={materials.Physical12} position={[20.21, -48.47, 0.65]} />
              <mesh geometry={nodes.Tongue_Lining.geometry} material={materials.d3ebc} position={[20.21, -48.47, 0.65]} />
            </group>
            <mesh geometry={nodes.Binding.geometry} material={materials.Physical7} />
            <mesh geometry={nodes.Insole.geometry} material={materials.Physical15} />
            <mesh geometry={nodes.Lining.geometry} material={materials.Physical11} />
            <mesh geometry={nodes.Tag.geometry} material={materials.ffffff} position={[-15.88, 44.52, -24.34]} />
            <mesh geometry={nodes.Vamp.geometry} material={nodes.Vamp.material} />
            <mesh geometry={nodes.Plugs.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.polySurface381.geometry} material={materials.b0b0b0} position={[9.17, 63.25, -14.78]} />
            <mesh geometry={nodes.polySurface382.geometry} material={materials.b0b0b0} position={[-9.44, 55.79, -14.22]} />
            <mesh geometry={nodes.polySurface383.geometry} material={materials.b0b0b0} position={[-18.66, 52.34, -13.55]} />
            <mesh geometry={nodes.polySurface384.geometry} material={materials.b0b0b0} position={[-27.95, 48.98, -12.7]} />
            <mesh geometry={nodes.polySurface385.geometry} material={materials.b0b0b0} position={[7.41, 65.1, 14.03]} />
            <mesh geometry={nodes.polySurface386.geometry} material={materials.b0b0b0} position={[-11.22, 58.21, 12.89]} />
            <mesh geometry={nodes.polySurface387.geometry} material={materials.b0b0b0} position={[-1.43, 61.81, 13.27]} />
            <mesh geometry={nodes.polySurface388.geometry} material={materials.b0b0b0} position={[-20.21, 54.94, 12.28]} />
            <mesh geometry={nodes.polySurface389.geometry} material={materials.b0b0b0} position={[-29.67, 51.27, 11.98]} />
            <mesh geometry={nodes.polySurface390.geometry} material={materials.b0b0b0} position={[-0.28, 59.54, -14.63]} />
            <mesh geometry={nodes.polySurface370.geometry} material={materials.b0b0b0} position={[109.3, 61.76, -0.46]} />
            <mesh geometry={nodes.polySurface392.geometry} material={materials.b0b0b0} position={[92.65, 44.44, -0.95]} />
            <mesh geometry={nodes.Lace_1_1.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_2.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_3.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_4.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_5.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_6.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.Lace_7.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes._polySurface207.geometry} material={materials.Physical2} />
            <mesh geometry={nodes.polySurface394.geometry} material={materials.Physical1} />
            <mesh geometry={nodes.pasted__stitch24.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.pasted__stitch25.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.pasted__stitch26.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.pasted__stitch27.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch10.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch11.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch14.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch15.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch16.geometry} material={materials.b0b0b0} position={[-21.2, 47.81, -1.23]} />
            <mesh geometry={nodes.stitch17.geometry} material={materials.b0b0b0} position={[0, -0.77, -0.12]} />
            <mesh geometry={nodes.stitch18.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch19.geometry} material={materials.b0b0b0} />
            <mesh geometry={nodes.stitch20.geometry} material={materials.b0b0b0} />
          </group>
        </group>
      </group>
    </group>
  )
}

useGLTF.preload('/model.glb')

Enter fullscreen mode Exit fullscreen mode

P.S. Each model has it's own unique code structure. So do not panic if your generated code does not look like mine.

Then you will have to move the glb file into public directory to make it accessible for React.

Designing the scene

In order to design a 3D scene, you will need to add a camera, light and the model to the scene. you need to edit App.js and make it look like this:

import './App.css';
import { React, Suspense } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Model } from './Model';

function App() {
  return (
    <div className='App'>
      <Canvas
          camera={{ position: [100, 80, 50], fov: 11 }}
          style={{
          width: '20vw',
          height: '38vh',
          border: '1px solid black'
          }}
      >
          <ambientLight intensity={1.25} />
          <directionalLight position={[0,-2,0]} intensity={2} />
          <directionalLight position={[0,2,0]} intensity={2} />
          <directionalLight position={[-2,0,0]} intensity={2} />
          <directionalLight position={[2,0,0]} intensity={2} />

          <Suspense fallback={null}>
          <Model position={[0, 0, 0]} />
          </Suspense>
          <OrbitControls enableZoom={false}/>
      </Canvas>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

P.S. Each model have a different scale size, so the previous variables might not be suitable for all models.

To overcome this issue you will need to modify camera position and field of view (fov). Also, if your scene is too bright or dark you will have to modify the light intensity.

Also, if you want to enable zoom functionality you will have to replace enableZoom in OrbitControls with minDistance={} and maxDistance={}. Again, you will need to enter values according to model scale.

Then, you will need to edit App.css to make the scene in the middle of the screen.

body{
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
}
Enter fullscreen mode Exit fullscreen mode

After adding the CSS, your website will look like this:

Result

Designing the product card

You are free to design any UI/UX you think it's suitable for the project. I have decided to use nuemorphic design by the artist Maria Marin.

Result

Finally, lets run the project by using this command:

npm start
Enter fullscreen mode Exit fullscreen mode

My product card looked like this:


Github: 3D-Product-Card
P.S. You will have to view it in fullscreen desktop mode because it's not responsive yet.

Also Read
Image Processing with JavaScript

Kindly follow me on
LinkedIn
GitHub
CodePen

Top comments (0)