3D Product Card With ReactJS
Tags: webdev, javascript, react, 3d
Published: 2023-02-17 07:34:07

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](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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjx5b58flu2wiswiw0yt.jpg)

## Setting up the environment
First, we need to create a new React application.
```
npx create-react-app 3d-product
```
Then, we need to install the dependencies that we will use in this project. We will use [Three.js](https://github.com/mrdoob/three.js) library, [@react-three/fiber](https://github.com/pmndrs/react-three-fiber) and [@react-three/drei](https://github.com/pmndrs/drei).

we can install them with npm
```
npm install three @react-three/fiber @react-three/drei
```
or by using yarn
```
yarn install three @react-three/fiber @react-three/drei
```
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
```
After running this command, new JavaScript file will be generated. It would be something like that:
```javascript
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')

```
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:
```javascript
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;
```
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.
```css
body{
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
}
```
After adding the CSS, your website will look like this:

![Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx5csaauo2ej5pyvmuex.jpg)

## 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](https://codepen.io/myacode/pen/PoqQQNM) by the artist [Maria Marin](https://dribbble.com/mya-design).

## Result
Finally, lets run the project by using this command:

```
npm start
```
My product card looked like this:

{% embed https://www.youtube.com/watch?v=kRTELvSsWlk %}
Github: [3D-Product-Card](https://github.com/MohamedSoliman21/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](https://dev.to/mohamedsoliman21/image-processing-with-javascript-3k67)

**Kindly follow me on**
[LinkedIn](https://www.linkedin.com/in/mohamed-soliman21/)
[GitHub](https://github.com/MohamedSoliman21)
[CodePen](https://codepen.io/MohamedSoliman21)