Core Concepts

User Context

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


useMotionGPUUserContext + setMotionGPUUserContext provide a split read/write API for namespaced, reactive shared state within the MotionGPU runtime. In React, useSetMotionGPUUserContext provides a context-bound setter for effect and event-handler writes. In Vue and Svelte, use setMotionGPUUserContext directly.

This hook is exported from the advanced entrypoint:

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

The same entrypoint also exports scheduler helpers (applySchedulerPreset, captureSchedulerDebugSnapshot) for runtime diagnostics workflows.

Why use user context?

Standard framework context APIs (for example Svelte setContext/getContext or React context values) are usually static by design and tied to component hierarchy. The Motion GPU advanced API provides:

  • Namespaced keys — multiple plugins write to different namespaces without conflicts.
  • Explicit read/write splituseMotionGPUUserContext reads; setMotionGPUUserContext writes.
  • Conflict strategiesskip, 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)
useSetMotionGPUUserContext() (namespace, valueOrFactory, options?) => T undefined (React write helper)
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/svelte/advanced'; import { useFrame } from '@motion-core/motion-gpu/svelte'; 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>

Effect/event writes

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

Full context read

<script lang="ts"> import { useMotionGPUUserContext } from '@motion-core/motion-gpu/svelte/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.