latest

@vctrl/hooks

NPM Downloads

Browser-side React hooks for loading, optimizing, and exporting 3D models. The runtime counterpart to @vctrl/core — built for React apps that need to handle 3D files directly in the browser.


Installation

npm install @vctrl/hooks
# or
pnpm add @vctrl/hooks

Hooks overview

HookImport pathDescription
useLoadModel@vctrl/hooks/use-load-modelLoad and parse GLTF/GLB/USDZ files from file lists or dropped directories
useOptimizeModel@vctrl/hooks/use-optimize-modelRun glTF-Transform optimizations on loaded models
useExportModel@vctrl/hooks/use-export-modelExport the current scene to GLB or glTF

useLoadModel

Loads 3D files and exposes the parsed Three.js Object3D scene.

import { ModelProvider, useLoadModel } from '@vctrl/hooks/use-load-model'
 
function Uploader() {
  const { load, file, isFileLoading } = useLoadModel()
 
  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault()
    void load(Array.from(e.dataTransfer.files))
  }
 
  if (isFileLoading) return <p>Loading...</p>
  if (file.model) return <p>Model loaded: {file.name}</p>
 
  return <div onDrop={handleDrop} onDragOver={(e) => e.preventDefault()}>Drop a file</div>
}
 
export default function App() {
  return (
    <ModelProvider>
      <Uploader />
    </ModelProvider>
  )
}

Context and direct usage

useLoadModel can be used in two ways:

  1. Context mode (recommended) — wrap your app with ModelProvider, then consume with useModelContext() anywhere in that tree.
  2. Direct mode — call useLoadModel() outside a provider to manage a local model state.

Return values

ValueTypeDescription
fileModelFile | nullLoaded file metadata + Three.js model
isFileLoadingbooleanTrue while loading/parsing
progressnumberProgress value from 0-100
load(filesOrDirectories)Promise<void>Load files or dropped FileSystemDirectoryHandle entries
loadFromData(options)Promise<SceneLoadResult>Load already-resolved server scene payload
loadFromServer(options)Promise<SceneLoadResult>Fetch and load scene from API endpoint
reset() => voidClear the current model
on / offEvent helpersSubscribe/unsubscribe to loader lifecycle events
optimizerOptimizerIntegrationReturn | nullPopulated when hook is called with useOptimizeModel()

useLoadModel events

on and off support these typed events:

EventPayload
multiple-modelsFile[]
not-loaded-filesFile[]
load-startnull
load-progressnumber
load-completeModelFile | null
load-resetnull
load-errorError | unknown
server-load-startstring
server-load-completeSceneLoadResult
server-load-errorError | unknown

Scene loading option types

loadFromServer(options) uses:

FieldTypeDescription
sceneIdstringScene identifier to fetch
serverOptionsServerOptionsEndpoint/auth/header configuration
applySettingsbooleanWhether scene settings are applied after load

loadFromData(options) uses:

FieldTypeDescription
sceneIdstring | undefinedOptional scene identifier
sceneDataServerSceneDataAlready-resolved payload containing glTF/settings/assets
applySettingsbooleanWhether scene settings are applied after load

useOptimizeModel

Runs mesh simplification and texture compression using glTF-Transform in a Web Worker.

import { useOptimizeModel } from '@vctrl/hooks/use-optimize-model'
import { useLoadModel } from '@vctrl/hooks/use-load-model'
 
function Optimizer() {
  const optimizer = useOptimizeModel()
  const { file, optimizer: integrated } = useLoadModel(optimizer)
 
  const handleOptimize = async () => {
    if (!file?.model || !integrated) return
    await integrated.applyOptimization(integrated.simplifyOptimization, {
      ratio: 0.6,
      error: 0.001
    })
  }
 
  return (
    <button onClick={handleOptimize} disabled={optimizer.loading}>
      {optimizer.loading ? 'Optimizing...' : 'Optimize model'}
    </button>
  )
}

Key API surface

Method / StateTypeDescription
load(model)(model: Object3D) => Promise<void>Load a Three.js scene into optimizer
loadFromServerSceneData(sceneData)Promise<void>Initialize optimizer from server scene payload
applyOptimization(fn, opts?)<T>(fn?: (opts?: T) => Promise<void>, opts?: T) => Promise<void>Apply optimization and sync optimized model back into loader state
simplifyOptimization(options?)Promise<void>Simplify mesh geometry
dedupOptimization(options?)Promise<void>Deduplicate model data
quantizeOptimization(options?)Promise<void>Quantize vertex attributes
normalsOptimization(options?)Promise<void>Recompute/normalize normals
texturesOptimization(options?)Promise<void>Run texture compression flow
getModel()Promise<Uint8Array | null>Export current optimized model as GLB binary
report / infoObjectsOptimization metrics and derived stats
loading / errorStateOptimization status

Optimization option types

useOptimizeModel methods map directly to @vctrl/core/model-optimizer option types:

MethodOption fields
simplifyOptimizationratio?: number, error?: number
dedupOptimizationtextures?: boolean, materials?: boolean, meshes?: boolean, accessors?: boolean
quantizeOptimizationquantizePosition?: number, quantizeNormal?: number, quantizeColor?: number, quantizeTexcoord?: number
normalsOptimizationoverwrite?: boolean
texturesOptimizationresize?: [number, number], targetFormat?: 'webp' | 'jpeg' | 'png', quality?: number, requestTimeoutMs?: number, maxTextureUploadBytes?: number, maxRetries?: number, maxConcurrentRequests?: number, serverOptions?: ServerOptions

serverOptions is provided by @vctrl/core and supports endpoint, API key, and headers.


useExportModel

Exports the current scene (from ModelProvider context) to a downloadable file.

import { useExportModel } from '@vctrl/hooks/use-export-model'
import type { ModelFile } from '@vctrl/hooks/use-load-model'
 
function ExportButton({ file }: { file: ModelFile | null }) {
  const { handleThreeGltfExport } = useExportModel()
 
  return (
    <button onClick={() => void handleThreeGltfExport(file, true)}>
      Download GLB
    </button>
  )
}

Methods

MethodDescription
handleThreeGltfExport(file, binary)Export a loaded Three.js model to .glb or zipped .gltf bundle
handleDocumentGltfExport(document, file, binary?, download?)Export from a glTF-Transform Document

binary = true writes .glb; binary = false writes a zipped .gltf package.


Additional exports

  • ServerCommunicationService from @vctrl/hooks
  • reconstructGltfFiles from @vctrl/hooks
  • ModelProvider and useModelContext from @vctrl/hooks/use-load-model
  • Shared types such as ModelFile, SceneLoadResult, and ServerSceneData

ServerCommunicationService methods:

MethodDescription
request(config)Generic request helper
get(endpoint, serverOptions?)Convenience GET
post(endpoint, body, serverOptions?)Convenience JSON POST
postFormData(endpoint, formData, serverOptions?)Convenience FormData POST

ServerRequestConfig fields:

FieldType
endpointstring
method'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
bodyFormData | Record<string, unknown> | string
serverOptionsServerOptions
contentTypestring

Peer dependencies

PackageVersion
react^18 || ^19
three^0.170
@react-three/fiber^9

Source

Full source and detailed README in packages/hooks/.