Core Concepts

User Context

Advanced read/write API for namespaced, reactive shared state within the MotionGPU runtime.


useMotionGPUUserContext
+
setMotionGPUUserContext
provide a split read/write API for namespaced, reactive shared state within the MotionGPU runtime. This is designed for plugin-like patterns where multiple independent components need to coordinate shared data.

This hook is exported from the advanced entrypoint:

import { setMotionGPUUserContext, useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';
import { setMotionGPUUserContext, useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';

The same entrypoint also exports scheduler helpers (

applySchedulerPreset
,
captureSchedulerDebugSnapshot
) for runtime diagnostics workflows.

Why use user context?

Standard Svelte context (

setContext
/
getContext
) is read-only once set and tightly coupled to component hierarchy. The MotionGPU advanced API provides:

  • Namespaced keys — multiple plugins write to different namespaces without conflicts.
  • Explicit read/write split
    useMotionGPUUserContext
    reads;
    setMotionGPUUserContext
    writes.
  • Conflict strategies
    skip
    ,
    replace
    , or
    merge
    when a namespace already exists.
  • Reactive reads — values are backed by the
    user
    store from
    useMotionGPU()
    .

API

Read the entire user context

const allState = useMotionGPUUserContext();
// Returns CurrentReadable<Record<string | symbol, unknown>>
const allState = useMotionGPUUserContext();
// Returns CurrentReadable<Record<string | symbol, unknown>>

Read a namespace

const orbit = useMotionGPUUserContext<{ enabled: boolean; speed: number }>('orbit-controls');
// Returns a CurrentReadable store for key 'orbit-controls'
const orbit = useMotionGPUUserContext<{ enabled: boolean; speed: number }>('orbit-controls');
// Returns a CurrentReadable store for key 'orbit-controls'

Write a namespace

const result = setMotionGPUUserContext('orbit-controls', () => ({
  enabled: true,
  speed: 1.0
}), { existing: 'skip' });
const result = setMotionGPUUserContext('orbit-controls', () => ({
  enabled: true,
  speed: 1.0
}), { existing: 'skip' });

The second argument is a value or factory function. Factory is only called if needed (based on conflict strategy). The return value is the effective value currently stored under the namespace.

SetMotionGPUUserContextOptions

Field Type Default Description
existing
'skip'
|
'replace'
|
'merge'
'skip'
What to do if the namespace already has a value

Conflict strategies

Strategy Behaviour
'skip'
If namespace exists, ignore the new value. Factory is not called.
'replace'
Overwrite the namespace with the new value.
'merge'
Shallow-merge new value into existing value (
{ ...existing, ...new }
).

Return value

The return value depends on the overload:

Overload Return type
useMotionGPUUserContext()
CurrentReadable<Record<string
symbol, unknown>>
useMotionGPUUserContext(namespace)
CurrentReadable<T
undefined>
(reactive namespace view)
setMotionGPUUserContext(namespace, valueOrFactory, options?)
T
undefined
(the effective value at namespace)

Practical patterns

Plugin initialization (skip if exists)

// PluginA.svelte — initializes only if no one else has set it
setMotionGPUUserContext('config', () => ({
  quality: 'high',
  debugGrid: false
}), { existing: 'skip' });
// PluginA.svelte — initializes only if no one else has set it
setMotionGPUUserContext('config', () => ({
  quality: 'high',
  debugGrid: false
}), { existing: 'skip' });

Plugin override (replace)

// DebugPanel.svelte — force-sets debug config
setMotionGPUUserContext('config', () => ({
  quality: 'low',
  debugGrid: true
}), { existing: 'replace' });
// DebugPanel.svelte — force-sets debug config
setMotionGPUUserContext('config', () => ({
  quality: 'low',
  debugGrid: true
}), { existing: 'replace' });

Partial updates (merge)

// QualityButton.svelte — updates only the quality field
setMotionGPUUserContext('config', () => ({
  quality: 'medium'
}), { existing: 'merge' });
// QualityButton.svelte — updates only the quality field
setMotionGPUUserContext('config', () => ({
  quality: 'medium'
}), { existing: 'merge' });

Consuming shared state

<script lang="ts">
  import { setMotionGPUUserContext, useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';
  import { useFrame } from '@motion-core/motion-gpu';

  type Config = { quality: string; debugGrid: boolean };
  const config = useMotionGPUUserContext<Config>('config');

  useFrame((state) => {
    if (config.current?.debugGrid) {
      // Apply debug grid logic
    }
  });

  function setMedium() {
    setMotionGPUUserContext('config', { quality: 'medium' }, { existing: 'merge' });
  }
</script>
<script lang="ts">
  import { setMotionGPUUserContext, useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';
  import { useFrame } from '@motion-core/motion-gpu';

  type Config = { quality: string; debugGrid: boolean };
  const config = useMotionGPUUserContext<Config>('config');

  useFrame((state) => {
    if (config.current?.debugGrid) {
      // Apply debug grid logic
    }
  });

  function setMedium() {
    setMotionGPUUserContext('config', { quality: 'medium' }, { existing: 'merge' });
  }
</script>

Full context read

<script lang="ts">
  import { useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';

  const ctx = useMotionGPUUserContext();
</script>
<script lang="ts">
  import { useMotionGPUUserContext } from '@motion-core/motion-gpu/advanced';

  const ctx = useMotionGPUUserContext();
</script>

Under the hood

User context is stored in the

user
CurrentWritable
store exposed by
useMotionGPU()
.
useMotionGPUUserContext
provides namespaced read views, while
setMotionGPUUserContext
applies explicit writes with conflict-resolution rules.