Configure your Next.js Typescript project for custom shader materials and glsify in React Three Fiber

Matthew FrawleyMatthew FrawleyJanuary 12th, 2025

Let's walk through the necessary setup for working with GLSL shaders in Next.js, React Three Fiber and TypeScript.

This configuration allows you to use glslify and load .frag, .vert, or .glsl files without compile-time errors. We'll also cover the correct use of Typescript within the React Component.

1. Install Dependencies

You have a Next.js project set up. You'll need these packages to load and process GLSL files:

npm install raw-loader glslify glslify-loader

2. Configure Your Next.js Build

By default, Next.js won't know what to do with .frag, .vert, or .glsl files. We need to tell Webpack how to handle them. In Next.js you can modify the webpack configuration inside next.config.js (or next.config.mjs).

next.config.mjs
/** @type {import('next').NextConfig} */
 
const nextConfig = {
  webpack: (config) => {
    config.module.rules.push({
      test: /\.(glsl|vs|fs|vert|frag)$/,
      use: ['raw-loader', 'glslify', 'glslify-loader'],
    })
    return config
  },
}
 
export default nextConfig

3. Add a Type Declaration in your root directory

This file tells TypeScript that the GLSL extensions should be treated as string modules.

shader.d.ts
declare module '*.frag' {
  const value: string;
  export default value;
}
 
declare module '*.vert' {
  const value: string;
  export default value;
}
 
declare module '*.glsl' {
  const value: string;
  export default value;
}

4. Importing and exporting inside shader files

Now you can import other GLSL files inside your shader files.
For example, you might want to use a noise function from the glsl-noise package:

shader.frag
#pragma glslify: noise = require('glsl-noise/simplex/3d')

You can also export functions/variables for re-use:

myfunction.glsl
#pragma glslify: rotation3dZ = require(glsl-rotate/rotation-3d-z)
#pragma glslify: rotation3dY = require(glsl-rotate/rotation-3d-y)
#pragma glslify: rotation3dX = require(glsl-rotate/rotation-3d-x)
 
vec3 customRotate(inout vec3 position, in float angle) {
    position *= rotation3dZ(-angle * 2.0);
    position *= rotation3dY(angle * 4.0);
    position *= rotation3dX(-angle);
    return position;
}
 
#pragma glslify: export(customRotate)

5. Setting up your component

We're all setup to import shader files in your React components and create our custom shaderMaterial using the @react-three/drei helper function.

MyShaderComponent.tsx
'use client'
import { shaderMaterial } from '@react-three/drei';
import { extend, useFrame } from '@react-three/fiber';
import { ShaderMaterial } from 'three';
import { type FC } from 'react';
 
import vertexShader from './shader.vert';
import fragmentShader from './shader.frag';
 
type Uniforms {
  uTime: number
}
 
const DEFAULT_UNIFORMS: Uniforms = {
  uTime: 0
}
 
const MyShaderMaterial = shaderMaterial(
  DEFAULT_UNIFORMS,
  vertex,
  fragment
);
 
extend({ MyShaderMaterial });
 
declare module '@react-three/fiber' {
  interface ThreeElements {
    myShaderMaterial: ShaderMaterialProps & Uniforms
  }
}
 
// ... Component below
MyShaderComponent.tsx
const MyShaderComponent: FC = () => {
  const shaderMaterial = useRef<ShaderMaterial & Uniforms>(null)
 
  useFrame(({ clock }) => {
    if (!shaderMaterial.current) return
    shaderMaterial.current.uTime = clock.elapsedTime
  })
 
  return (
    <mesh>
      <planeGeometry args={[1,1,1,1]} />
      <myShaderMaterial
        attach="material" // Not needed but added for reference
        ref={shaderMaterial}
        key={MyShaderMaterial.key}
        uTime={0}
      
      />
    </mesh>
  )
}

Summary

  • Use ShaderMaterial from Three and our Uniforms type for the shader material ref
  • Update frequently changing uniform(s) inside the useFrame hook
  • Attach the shader material to a mesh
  • Add a key prop to enable hot-reloading when the shader code is edited

Summary

In just a few steps your Next.js TypeScript app is all set up to work with GLSL shaders in React Three Fiber.

Explore a working example here: Scrolling Background Gradient

Happy shading! 🖌️


Thanks for reading, Matt ✌️