This plugin generates type-safe glTF file representations in TypeScript and optimizes the loading and bundling of models in web projects, while being bundler-agnostic (Vite, Rollup, Webpack, esbuild, Rspack, ...).
This plugin scans all model files in the project source, deconstructs the glTF JSON representation, and places generated type files next to them. It uses three.js to parse the glTF files, including modifications like path resolutions etc.
With this plugin you get:
- ✅ Type safe glTF file representations with correct inner three.js types
like
Object3D
,Mesh
, etc.. - ✅ Building will fail if a model is missing due to type-safe workflow.
- ✅ Only the used models are bundled in the final product, not all included in your dev project.
⚠️ Detects and handles Draco Compression during type generation automatically, see Draco Compression handling below for more information.- ✅ Works with glTF Seperate (
.gltf
+.bin
+ textures) and glTF Embedded (only.gltf
) files, see glTF versions and representations below for more information. - ✅ ESM ready.
- ✅ Bundler agnostic thanks to Unplugin, so use it with your favorite one, but see chapter Bundler and Framework Compatibility below for more details:
- ✅ Use with your loved framework like:
It will run when your dev server starts and also when you build your project.
If you like this plugin and want to support us, we would be very happy to see you as a sponsor on GitHub ❤️
You can find the Sponsor
button on the top right of the
GitHub project page.
Thanks a lot for the support <3
For development-related information, including setup instructions for contributors, please refer to the Developer README.
-
Install with your package manager (we use pnpm and recommend it):
# choose only one pnpm add -D @todde.tv/gltf-type-toolkit npm install --save-dev @todde.tv/gltf-type-toolkit yarn add --dev @todde.tv/gltf-type-toolkit bun add --dev @todde.tv/gltf-type-toolkit
-
Extend your
.gitignore
file to exclude generated files:# Generated glTF model files *.gltf.d.ts *.gltf.js
-
Add the plugin to your bundler, for example with Vite:
// vite.config.ts import gltf from '@todde.tv/gltf-type-toolkit/vite.js' export default defineConfig({ plugins: [ gltf({ // Plugin options are defined in: `@todde.tv/gltf-type-toolkit/src/entries/types.ts` /** * Module that provides an instance of a three.js GLTFLoader as the default export. */ // customGltfLoaderModule: '@/utils/customGltfLoader.ts', /** * Print extra information. */ // verbose: true, }), ], })
Here's an example of how to use the plugin. You can customize this example to suit your needs, such as saving models to different folders, changing paths, or adjusting how the model is handled after import.
-
Install the plugin (See Installation above).
-
Add a model to your project, here in the project source under
@/assets/models
. We copy the following in it:MyModel.gltf
The glTF JSON representation that describes the model.MyModel.bin
The binary file of the model, compressed with the Draco Compression.MyModel-texture1.png
A texture that the model uses.MyModel-texture2.png
A second texture that the model uses.
-
Start your dev to generate all files. With our example model we get:
@/assets/models/MyModel.gltf @/assets/models/MyModel.bin @/assets/models/MyModel-texture1.png @/assets/models/MyModel-texture2.png +@/assets/models/MyModel.gltf.d.ts <- the typing +@/assets/models/MyModel.gltf.js <- actual code with node get helper function and scene/ model graph representation
-
Import the type safe model in your code and use it, e.g.:
// yourCustomFile.ts import { getNode, MyModelScene } from '@/assets/models/MyModel.gltf.js' // load the main model, so to say the wrapper in the scene const model = await getNode(MyModelScene) // load a child const innerModel = await getNode(MyModelScene.someInnerModel) // work with a model innerModel.receiveShadow = true innerModel.castShadow = true
Thanks to Unplugin, we support a wide variety of bundlers and frameworks, resulting in the following compatibility in our project:
(Legend: 🟢 Tested & Supported | 🟡 not yet tested | 🔴 Not Supported)
Bundler | Status | Note |
---|---|---|
vite | 🟢 | |
esbuild | 🟢 | |
rollup | 🟢 | |
webpack | 🟢 | |
rspack | 🟢 | |
astro | 🟡 | |
nuxt | 🟡 | |
rolldown | 🟡 | |
farm | 🔴 | Tested & not working, help is welcome |
We only support glTF 2.0, so use glTF Seperate (.gltf
+ .bin
+ textures) or glTF Embedded (only .gltf
) files.
We do not support glTF 1 nor glTF Binary (.glb
) files.
This plugin can handle the Draco Compression during the type generation and handles your draco compressed models just fine.
On runtime though, you have to handle the draco compression yourself depending on your architecture and setup.
You can do this by creating a custom gltfLoader
and pass it in the customGltfLoaderModule
prop whn initializing
the plugin. In this loader, you can handle the draco loader.
For example with Vite:
// vite.config.ts
import gltf from '@todde.tv/gltf-type-toolkit/vite.js'
export default defineConfig({
plugins: [
gltf({
customGltfLoaderModule: '@/utils/gltfLoader.ts',
}),
],
})
// @/utils/gltfLoader.ts
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
const dracoLoader = undefined // use an existing draco loader or create a custom one by extending `DRACOLoader`
const gltfLoader = new GLTFLoader().setDRACOLoader(dracoLoader)
export default gltfLoader
On runtime it runs useGLTF
under the hood. So no extra layer and therefore correct objects that
three.js would give you when importing it yourself in runtime.
To achieve this, we patch a useGLTF
from three.js
to be able to run it in CLI because originally you can only use
this in a browser runtime.
So we do not parse the glTF JSON file ourself but work with the representation after the
three.js useGLTF
parsing. For code completion and for working with the
scene/ model graph, we store the paths in the graph by caching the child indices in the childs
array from each
object. With this, we can O(1) look up what the user requests without the need of traversing - neither depth first
(default approach from three.js), nor breath first.
One of the biggest challenges was making the plugin bundler agnostic bc not every bundler handles inner path references the same. Therefore, we have to reconstruct the buffers that link the glTF file with the binary and textures by text string & replace - kinda hacky, but reliable and the additional runtime is only performed in dev and build, not in the production runtime.
If you have problems, maybe one of the following will help:
- Delete your build output folder (maybe some old builds copied model files in there and our plugin is now scanning them).
Project founder & head of project:
Honorable mentions to people that helped this project:
- Andreas Fehn as contributor who helped incredible with the project and the magic behind the core. Thank you <3
Respectable mentions to projects that helped this project:
- [currently none]
Used programs/ softwares, services and dependencies - besides the ones in ./package.json
:
- GitHub Copilot was used in private mode for programming questions.
Used assets/ materials including images and 3D models:
- [currently none]
Copyright (c) 2025-PRESENT Thorsten Seyschab
This project is licensed under the MIT License, see the LICENSE.md
file for more details.