Loading Assets
Whether its textures, materials or 3D models, assets are a key part of any 3D app. And generally speaking, they take the most time to load. PlayCanvas React provides a set of specialized hooks for loading different types of assets, as well as a utility function for loading assets. This helps you get up to speed quickly but with granular control of how and when assets are loaded.
Basic Usage
The simplest way to load an asset is to use one of the specialized hooks. There are special hooks for models, gaussian splats, textures, environment map, fonts and more. They all have a similar shape and return a PlayCanvas Asset.
const { asset } = useModel('model.glb');
return <Render asset={asset} />;
In this example, we're loading a model using the useModel hook and rendering it using the <Render/> component.
- Demo
- Code
import { Entity } from '@playcanvas/react';
import { Render } from '@playcanvas/react/components';
import { useModel } from '@playcanvas/react/hooks';
export const ModelLoading = () => {
// Load the selected model
const { asset, error } = useModel('/assets/statue.glb');
// If there is an error, log it
if (error) {
console.error('Error loading model:', error);
return null;
}
// If the asset is not loaded, return null
if (!asset) return null;
// If the asset is loaded, render it
return (
<Entity position={[0, -0.5, 0]} scale={[0.1, 0.1, 0.1]}>
<Render asset={asset} />
</Entity>
);
};
Preloading
The asset hooks also return additional loading info and error states, so you can fallback to a preloader while loading or display an error message if the asset fails to load.
import { useModel } from '@playcanvas/react/hooks';
export function ModelViewer() {
const { asset, loading, error } = useModel('model.glb');
// If the asset is still loading, show a loading spinner
if (loading) return <LoadingSpinner />;
// If there is an error, show an error message
if (error) return <ErrorMessage message={error} />;
// If the asset is loaded, render it
return <Render type="asset" asset={asset} />;
}
Loading with Props
Some assets accept additional properties to customize how they are loaded. You can pass these properties to the hook as a second argument.
// Load a texture with specific settings
const { asset } = useTexture('texture.jpg', {
mipmaps: true,
anisotropy: 16,
type: 'rgba'
});
Asset hooks
There are different hooks for loading different types of assets. You can create more advanced hooks by wrapping the useAsset hook.
useModelfor loading 3D GLTF/GLB modelsuseTexturefor loading texturesuseSplatfor loading Gaussian SplatsuseEnvAtlasfor loading environment atlasesuseAssetfor loading any type of assetuseFontfor loading fonts
Asset Caching
Assets are cached by default to avoid reloading the same file multiple times. This means you're not duplicating on memory, but you'll need to ensure assets are correctly unloaded when they're no longer needed.
import { useModel } from '@playcanvas/react/hooks';
import { useEffect } from 'react';
export function UnloadingModelViewer() {
const { asset, loading, error } = useModel('model.glb');
useEffect(() => {
return () => asset?.unload();
}, [asset]);
if (!asset) return null;
return <Render type="asset" asset={asset} />;
}
Unloading an asset will remove it globally. This will affect other components that are using the same asset.
Custom Loading States
You can use placeholders or custom loaders while assets load by checking the loading state of an asset. This gives you granular control.
import { Entity, Render } from '@playcanvas/react/components';
// A component that displays a model with a custom loading state
export function ModelWithCustomLoading() {
const { asset: plane, loading: planeLoading } = useModel('plane.glb');
const { asset: car, loading: carLoading } = useModel('car.glb');
if (planeLoading || carLoading) return <LoadingSpinner />;
return <>
<Entity name="car">
<Render asset={car} />
</Entity>
<Entity name="plane">
<Render asset={plane} />
</Entity>
</>
}
Progressive Loading
The loading hooks also provide a simple mechanic to progressively load assets, so you can prioritize rendering quickly following up with high quality content later.
import { Entity, Render } from '@playcanvas/react/components';
// A component that displays a model with a custom loading state
export function ProgressiveAsset() {
const { asset: low } = useModel('./low-quality-model.sog'); // load the low quality asset
const { asset: high } = useModel(low && './high-quality-model.sog'); // load the high quality asset, when the low quality is loaded
if (!low && !high ) return null;
return <Gsplat asset={high || low} />
}
Data Fetching Libraries
If you need more advanced caching or loading strategies, you can integrate with libraries like React Query or SWR or any other Promise based library using the fetchAsset utility.
import { fetchAsset } from '@playcanvas/react/utils';
import { useQuery } from '@tanstack/react-query';
function useQueryModel(src: string) {
const query = useQuery({
queryKey: ['asset', src],
// 'container' is the type of asset we're loading (e.g., model, texture, etc.)
queryFn: () => fetchAsset({ app, url: src, type: 'container' })
});
return query;
}
export function ModelWithQuery() {
const { data: asset, isLoading } = useQueryModel('model.glb');
if (isLoading) return <LoadingSpinner />;
return <Render type="asset" asset={asset} />;
}
See the React Query documentation and SWR documentation for more information on how to use it.
Suspense Integration
React Query and SWR have built-in support for Suspense, which allows you to handle loading states in a more declarative way.
import { fetchAsset } from '@playcanvas/react/utils';
import { useQuery } from '@tanstack/react-query';
function useSuspendedQueryModel(src: string) {
const query = useQuery({
queryKey: ['asset', src],
queryFn: () => fetchAsset({ app, url: src, type: 'container' }),
suspense: true
});
return query;
}
export function ModelWithQuery() {
const { data: asset } = useSuspendedQueryModel('model.glb');
return <Render type="asset" asset={asset} />;
}
You can read more about Suspense in the React documentation, as well as the React Query documentation and SWR documentation.