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-loader2. 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).
/** @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 nextConfig3. Add a Type Declaration in your root directory
This file tells TypeScript that the GLSL extensions should be treated as string modules.
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:
#pragma glslify: noise = require('glsl-noise/simplex/3d')You can also export functions/variables for re-use:
#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.
'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 });
// ... Component belowconst 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
ShaderMaterialfrom Three and ourUniformstype for the shader material ref - Update frequently changing uniform(s) inside the
useFramehook - Attach the shader material to a mesh
- Add a
keyprop 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! 🖌️