threlte logo
@threlte/core

useLoader

A hook to load data with an arbitrary THREE loader class. The result of load is cached and subsequent calls with the same arguments will yield the same return value (i.e. the same reference) across the entire Threlte app. The hook can use async loaders (using loader.loadAsync) as well as sync/callback-based loaders (using loader.load) and will default to the async version if available.

const loader = useLoader(AudioLoader)
const soundA = loader.load('audio/ambient_ocean.ogg')
// -> `soundA` is an AsyncWritable<AudioBuffer> that
// resolves/is populated once the asset is loaded.

// somewhere else …
const soundB = loader.load('audio/ambient_ocean.ogg')
// -> `soundB` is also an AsyncWritable<AudioBuffer>
// that resolves/is populated with the same reference
// as `soundA` once the asset is loaded.

Instantiating A Loader

A loader must always be instantiated at the top level of a component. This is because the loader is depending on an application-wide cache:

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const loader = useLoader(TextureLoader)
</script>

A loader can be extended, to add custom features:

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const loader = useLoader(CustomTextureLoader, {
    extend: (loader) => {
      // do something with the loader, e.g. add DRACO support for
      // GLTFLoader or add custom headers for TextureLoader.
    }
  })
</script>

If the constructor of a particular loader accepts arguments, they must be passed as an array to the option args of the second argument to useLoader:

<script>
  import { useLoader, useThrelte } from '@threlte/core'
  import { SplatLoader } from '@pmndrs/vanilla'

  const { renderer } = useThrelte()

  const loader = useLoader(SplatLoader, {
    args: [renderer]
  })
  // This resembles the following:
  // const loader = new SplatLoader(renderer)
</script>

Loading An Asset

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const texture = useLoader(TextureLoader).load('path/to/texture.png')

  // A loader must always be instantiated at the top level of a component.
  const { load } = useLoader(TextureLoader)
  const onSomeEvent = () => {
    // To load an asset outside of the top level, use the `load` method.
    const texture = load('path/to/texture.png')
  }
</script>

The return type of load is a custom Threlte store called AsyncWritable and can be used as a regular Svelte store. Its initial value is undefined and will be updated once the asset is loaded.

The store also exposes the underlying promise methods then and catch and can therefore be directly awaited:

<script>
  import { useLoader, T } from '@threlte/core'
  import { TextureLoader } from 'three'

  const { load } = useLoader(TextureLoader)

  const onSomeEvent = async () => {
    // Load an asset and await the result.
    const texture = await load('path/to/texture.png')
  }
</script>

<!-- Or make use of Svelte's await block -->
{#await load('path/to/texture.png') then map}
  <T.MeshStandardMaterial {map} />
{/await}

Loading Multiple Assets

The function load of useLoader also accepts an array of paths in which case the return value is an array of loaded assets:

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const { load } = useLoader(TextureLoader)
  const textures = load(['texture1.png', 'texture2.png'])

  $: console.log($textures) // [Texture, Texture]
</script>

Keep in mind that the store will be updated and the promise resolves once all assets are loaded.

You can also provide a map of paths to load multiple assets at once. In this case the return value is a map of the loaded assets:

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const { load } = useLoader(TextureLoader)
  const textures = load({
    texture1: 'texture1.png',
    texture2: 'texture2.png'
  })

  $: console.log($textures) // { texture1: Texture, texture2: Texture }
</script>

Transforming The Result

Caching

The result of the transformation is cached and subsequent calls will return the same result.

The load method accepts an optional second argument to transform the result of the loader:

<script>
  import { useLoader } from '@threlte/core'
  import { TextureLoader } from 'three'

  const { load } = useLoader(TextureLoader)
  const texture = load('path/to/texture.png', {
    transform: (texture) => {
      // do something with the texture
      return texture
    }
  })
</script>

The return type of the transformation is used to infer the return type of the load method.