Skip to Content
🎉 @playcanvas/react 0.3.0! Now with React 19 support. Read more →
DocsGuideLoading Asset

Working with Assets

Assets are a core part of any 3D application, and they can also take a significant amount time to load, which can make your app feel sluggish. Knowing how to load and manage assets efficiently is key to a smooth user experience.

@playcanvas/react provides a set of specialized hooks for loading different types of assets, as well as a utility function for loading assets. For detailed API documentation, see the Asset Hooks reference

Basic Usage

The simplest way to load an asset is to use one of the specialized hooks.

model-viewer.tsx
import { useModel } from '@playcanvas/react/hooks'; export function ModelViewer() { const { asset } = useModel('model.glb'); // If the asset is not loaded, return null if (!asset) return null; // If the asset is loaded, render it return <Render type='asset' asset={asset} />; }

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.

model-viewer.tsx
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, show the container return <Container 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.

Advanced Patterns

Asset Caching

Assets are cached by default to avoid reloading the same file multiple times. This is useful for optimizing memory usage, but you’ll need to manually unload assets when they’re no longer needed.

unloading-model-viewer.tsx
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 <Container 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 create custom loading states for your assets:

loading-spinner.tsx
import { Script, Entity, Render } from '@playcanvas/react/components'; import { Script as PcScript } from 'playcanvas'; // Spins an entity class Spinner extends PcScript { update(dt: number) { this.entity.rotateLocal(0, 10 * dt, 0); } } // A component that displays a loading spinner export function LoadingSpinner() { return ( <Entity> <Script type={Spinner}/> <Render type='box'/> </Entity> ) } // A component that displays a model with a custom loading state export function ModelWithCustomLoading() { const { asset, loading } = useModel('model.glb'); if (loading) return <LoadingSpinner />; return <Container asset={asset} />; }

Data Fetching Libraries

If you need more advanced caching or loading strategies, you can integrate with libraries like React Query or SWR using the fetchAsset utility function.

model-with-query.tsx
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, src, 'container') }); return query; } export function ModelWithQuery() { const { data: asset, isLoading } = useQueryModel('model.glb'); if (isLoading) return <LoadingSpinner />; return <Container 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, src, 'container'), suspense: true }); return query; } export function ModelWithQuery() { const { data: asset } = useSuspendedQueryModel('model.glb'); return <Container asset={asset} />; }

You can read more about Suspense in the React documentation, as well as the React Query documentation and SWR documentation.

Last updated on