When Simple APIs Trip You Up

Even experienced Svelte developers occasionally stumble with context. The API is simple on the surface—just setContext and getContext—but subtle behaviors can lead to confusing bugs. Values that don’t update. Context that mysteriously returns undefined. Components that throw errors about initialization timing.

This article catalogs the most common context pitfalls, explains why they occur, and provides clear solutions. We’ll also cover debugging techniques to quickly diagnose context issues when they arise. Consider this your troubleshooting guide for everything context-related.

By understanding these pitfalls, you’ll write more robust context code and spend less time debugging mysterious failures.


Pitfall 1: Calling Context Functions Outside Initialization

The most common error: attempting to call setContext or getContext outside of component initialization.

The Error

Error: `setContext(...)` can only be called during component initialization
Error: `getContext(...)` can only be called during component initialization

What Causes It

Context functions must be called during the synchronous execution of your <script> block when the component first mounts. They cannot be called in:

  • Event handlers
  • $effect callbacks
  • setTimeout or setInterval callbacks
  • Promise .then() callbacks
  • After an await statement
  • Any asynchronous code

Examples of the Mistake

<script>
	import { setContext, getContext } from 'svelte'

	// ❌ Inside event handler
	function handleClick() {
		setContext('late', 'value') // Error!
	}

	// ❌ Inside effect
	$effect(() => {
		const value = getContext('something') // Error!
	})

	// ❌ Inside setTimeout
	setTimeout(() => {
		setContext('delayed', 'value') // Error!
	}, 0)

	// ❌ After await
	async function setup() {
		const data = await fetch('/api/data')
		setContext('data', data) // Error!
	}
	setup()

	// ❌ In Promise callback
	fetch('/api/config')
		.then((response) => response.json())
		.then((config) => {
			setContext('config', config) // Error!
		})
</script>

The Solution

Always capture or set context at the top level of your script, then use the captured values elsewhere:

<script>
	import { setContext, getContext } from 'svelte'

	// ✅ Set context during initialization with reactive state
	let config = $state(null)
	let loading = $state(true)

	setContext('config', {
		get data() {
			return config
		},
		get loading() {
			return loading
		}
	})

	// ✅ Capture context during initialization
	const theme = getContext('theme')
	const auth = getContext('auth')

	// Now use freely in event handlers
	function handleClick() {
		console.log('Theme:', theme.mode) // ✅ Works!
		console.log('User:', auth.user?.name) // ✅ Works!
	}

	// And in effects
	$effect(() => {
		document.body.classList.toggle('dark', theme.isDark) // ✅ Works!
	})

	// And with async operations
	async function loadConfig() {
		loading = true
		const response = await fetch('/api/config')
		config = await response.json() // Updates the context value!
		loading = false
	}
	loadConfig()
</script>

Why This Restriction Exists

Context is part of the component tree’s structure. When a child component initializes, it looks up the tree for context that ancestors have set. If context could be set later (in event handlers, etc.), children would have already tried to read it and failed.

Think of it like building a house: you need to install the plumbing (context) while building the walls (component initialization), not after the house is finished (runtime).


Pitfall 2: Reassigning State Objects

One of the trickiest pitfalls: breaking the connection between provider and consumers by reassigning state.

The Symptom

Context updates work initially, then suddenly stop. Some consumers see updates, others don’t. Or updates stop after a specific action.

What Causes It

When you provide a reactive object via context, both provider and consumers hold references to the same object. If you reassign the variable in the provider to a new object, consumers still reference the old object.

The Mistake

<!-- Provider.svelte -->
<script>
	import { setContext } from 'svelte'

	let user = $state({ name: 'Alice', score: 0 })
	setContext('user', user)

	function updateScore() {
		user.score++ // ✅ This works - mutating the object
	}

	function resetUser() {
		user = { name: 'Guest', score: 0 } // ❌ This breaks consumers!
		// Provider now points to NEW object
		// Consumers still point to OLD object
	}
</script>
<!-- Consumer.svelte -->
<script>
	import { getContext } from 'svelte'

	const user = getContext('user')
	// user references the ORIGINAL object
	// After resetUser(), this is orphaned
</script>

<p>{user.name}: {user.score}</p>
<!-- After resetUser(), still shows old values -->

Visual Explanation

Initial state:
Provider.user ───────┐
                     ├──> { name: 'Alice', score: 0 }
Consumer.user ───────┘

After user.score++ (mutation):
Provider.user ───────┐
                     ├──> { name: 'Alice', score: 1 }  ✅ Both see change
Consumer.user ───────┘

After user = { name: 'Guest', score: 0 } (reassignment):
Provider.user ───────────> { name: 'Guest', score: 0 }  ← NEW object

Consumer.user ───────────> { name: 'Alice', score: 1 }  ← OLD object (orphaned!)
                           ❌ Consumer never sees new object

The Solution

Always mutate properties, never reassign the entire object:

<script>
	import { setContext } from 'svelte'

	let user = $state({ name: 'Alice', score: 0 })
	setContext('user', user)

	function updateScore() {
		user.score++ // ✅ Mutation
	}

	function resetUser() {
		// ✅ Mutate properties individually
		user.name = 'Guest'
		user.score = 0
	}

	// ✅ Or use Object.assign for multiple properties
	function updateUser(updates) {
		Object.assign(user, updates)
	}
</script>

Alternative: Wrap in Container Object

If you need to replace the entire value, wrap it in a container:

<script>
	import { setContext } from 'svelte'

	let container = $state({ user: { name: 'Alice', score: 0 } })
	setContext('userContainer', container)

	function resetUser() {
		// ✅ Mutating container.user property
		container.user = { name: 'Guest', score: 0 }
	}
</script>

Consumer accesses via the container:

<script>
	const container = getContext('userContainer')
</script>

<!-- Access through container --><p>{container.user.name}: {container.user.score}</p>

Pitfall 3: Destructuring Reactive Values

Destructuring captures values at a point in time, breaking reactivity.

The Symptom

Variables from destructured context never update, even though the source changes.

The Mistake

<script>
	import { getContext } from 'svelte'

	const settings = getContext('settings')

	// ❌ Destructuring captures current VALUES
	const { theme, fontSize, language } = settings
	// theme = 'light' (a string, frozen in time)
	// fontSize = 16 (a number, frozen in time)
</script>

<!-- These never update! -->
<div class={theme}>
	<!-- Always 'light' -->
	<p style="font-size: {fontSize}px">
		<!-- Always 16 -->
		{language}
		<!-- Always the initial value -->
	</p>
</div>

Why This Happens

Destructuring is syntactic sugar for assignment:

const { theme } = settings
// Is equivalent to:
const theme = settings.theme // Copies the primitive value

For primitives (strings, numbers, booleans), you get a copy. For objects, you get a reference—but still captured at that moment.

Solutions

Solution 1: Don’t destructure, access directly

<script>
	const settings = getContext('settings')
	// Keep the object reference
</script>

<!-- Access properties directly - always current -->
<div class={settings.theme}>
	<p style="font-size: {settings.fontSize}px">
		{settings.language}
	</p>
</div>

Solution 2: Reactive “destructuring” with $derived

<script>
	const settings = getContext('settings')

	// ✅ Each $derived re-evaluates when settings changes
	let theme = $derived(settings.theme)
	let fontSize = $derived(settings.fontSize)
	let language = $derived(settings.language)
</script>

<!-- Now these update! -->
<div class={theme}>
	<p style="font-size: {fontSize}px">
		{language}
	</p>
</div>

Solution 3: Destructure methods only

Methods (functions) can be safely destructured—they’re references, not values:

<script>
	const cart = getContext('cart')

	// ✅ Safe - these are function references
	const { addItem, removeItem, clear } = cart

	function handleAdd(product) {
		addItem(product) // Works correctly
	}
</script>

Pitfall 4: Missing Provider

Consuming context that was never provided.

The Symptom

getContext returns undefined, leading to “Cannot read property X of undefined” errors when you try to use the context.

The Mistake

<!-- App.svelte - forgot the provider! -->
<script>
	import Dashboard from './Dashboard.svelte'
</script>

<!-- No AuthProvider wrapping Dashboard -->
<Dashboard />
<!-- Dashboard.svelte -->
<script>
	import { getContext } from 'svelte'

	const auth = getContext('auth') // Returns undefined!

	// Later...
	console.log(auth.user.name) // Error: Cannot read property 'user' of undefined
</script>

Solutions

Solution 1: Check with hasContext

<script>
	import { getContext, hasContext } from 'svelte'

	if (!hasContext('auth')) {
		throw new Error('Auth context not found. ' + 'Wrap this component in an AuthProvider.')
	}

	const auth = getContext('auth')
</script>

Solution 2: Provide defaults for optional context

<script>
	import { getContext, hasContext } from 'svelte'

	const defaultAuth = {
		user: null,
		isAuthenticated: false,
		login: () => console.warn('No auth provider'),
		logout: () => console.warn('No auth provider')
	}

	const auth = hasContext('auth') ? getContext('auth') : defaultAuth
</script>

Solution 3: Use createContext which throws

With Svelte 5.40+‘s createContext, the getter throws automatically:

// auth.svelte.ts
import { createContext } from 'svelte'

export const [getAuth, setAuth] = createContext<AuthContext>()

// Consumer.svelte
const auth = getAuth() // Throws "Context was not provided" if missing

Solution 4: Create a safe getter utility

// lib/context/utils.ts
import { getContext, hasContext } from 'svelte'

export function requireContext<T>(key: any, name: string): T {
	if (!hasContext(key)) {
		throw new Error(
			`${name} context is required but was not found. ` +
				`Ensure this component is rendered inside the appropriate provider.`
		)
	}
	return getContext(key)
}

// Usage
const auth = requireContext('auth', 'Auth')

Pitfall 5: Provider in Wrong Position

Provider is present but not an ancestor of the consumer.

The Symptom

Context returns undefined even though the provider exists in the app.

The Mistake

<!-- App.svelte -->
<script>
	import AuthProvider from './AuthProvider.svelte'
	import Dashboard from './Dashboard.svelte'
</script>

<!-- Provider and Dashboard are SIBLINGS, not parent-child -->
<AuthProvider />
<Dashboard />
<!-- Can't access auth context! -->

Context only flows downward from ancestors to descendants. Siblings can’t see each other’s context.

The Solution

Ensure the provider wraps the consumer:

<!-- App.svelte -->
<script>
	import AuthProvider from './AuthProvider.svelte'
	import Dashboard from './Dashboard.svelte'
</script>

<!-- Provider WRAPS Dashboard -->
<AuthProvider>
	<Dashboard />
	<!-- Now has access! -->
</AuthProvider>

Visual Debugging

❌ Wrong - siblings:
App
├── AuthProvider (sets 'auth')
└── Dashboard (getContext('auth') → undefined)

✅ Correct - ancestor/descendant:
App
└── AuthProvider (sets 'auth')
    └── Dashboard (getContext('auth') → ✓)

Pitfall 6: Wrong Key

Using a different key for get than what was used for set.

The Symptom

Context returns undefined even with correct provider/consumer relationship.

The Mistake

<!-- Provider.svelte -->
<script>
	import { setContext } from 'svelte'

	setContext('userAuth', authData)  // Key: 'userAuth'
</script>

<!-- Consumer.svelte -->
<script>
	import { getContext } from 'svelte'

	const auth = getContext('auth')  // Key: 'auth' ← Doesn't match!
	// Returns undefined
</script>

This is especially easy with:

  • Typos: 'user' vs 'users'
  • Case sensitivity: 'Auth' vs 'auth'
  • Different conventions: 'user-auth' vs 'userAuth'

Solutions

Solution 1: Export the key

// context/keys.ts
export const AUTH_KEY = 'auth'
export const CART_KEY = 'cart'
export const THEME_KEY = 'theme'
<!-- Both files import the same key -->
import {AUTH_KEY} from './context/keys' setContext(AUTH_KEY, value) // Provider getContext(AUTH_KEY)
// Consumer

Solution 2: Use Symbols

Symbols are unique, so key collisions are impossible:

// context/auth.svelte.ts
const AUTH_KEY = Symbol('auth')

export function createAuthContext() {
	return setContext(AUTH_KEY /* ... */)
}

export function getAuthContext() {
	return getContext(AUTH_KEY)
}

Solution 3: Use createContext

createContext manages keys internally:

// No key to get wrong!
export const [getAuth, setAuth] = createContext<AuthContext>()

Pitfall 7: Context in Dynamic Components

Components mounted outside the normal tree lose context.

The Symptom

Dynamically mounted components (modals, tooltips, portals) can’t access context.

The Mistake

<script>
	import { mount } from 'svelte'
	import Modal from './Modal.svelte'

	function openModal() {
		// Modal mounted at document.body, outside Svelte tree
		mount(Modal, { target: document.body })
		// Modal has NO access to any context!
	}
</script>

The Solution

Forward context explicitly using getAllContexts:

<script>
	import { mount, getAllContexts } from 'svelte'
	import Modal from './Modal.svelte'

	// Capture context during initialization
	const appContext = getAllContexts()

	function openModal() {
		mount(Modal, {
			target: document.body,
			context: appContext // Forward all context
		})
	}
</script>

Or create a portal component that handles this:

<!-- Portal.svelte -->
<script>
	import { mount, unmount, getAllContexts } from 'svelte'

	let { target = document.body, children } = $props()

	const context = getAllContexts()

	$effect(() => {
		// Create a wrapper component that re-provides context
		const wrapper = mount(ContextForwarder, {
			target,
			context,
			props: { content: children }
		})

		return () => unmount(wrapper)
	})
</script>

Pitfall 8: Expecting Reactivity Without $state

Plain objects in context aren’t reactive.

The Symptom

Context values don’t update when the source changes.

The Mistake

<script>
	import { setContext } from 'svelte'

	// Plain object, not reactive
	const user = { name: 'Alice', score: 0 }
	setContext('user', user)

	function updateScore() {
		user.score++ // Value changes in memory...
		// But Svelte doesn't know! No re-renders triggered.
	}
</script>

The Solution

Use $state for reactive context values:

<script>
	import { setContext } from 'svelte'

	// Reactive with $state
	let user = $state({ name: 'Alice', score: 0 })
	setContext('user', user)

	function updateScore() {
		user.score++ // ✅ Svelte tracks this, triggers re-renders
	}
</script>

Pitfall 9: Using Context for SSR-Sensitive Data

Context mutations during SSR can leak between requests.

The Symptom

Users see other users’ data. Data persists incorrectly across requests.

The Mistake

// lib/stores/global.svelte.ts

// Global mutable state - DANGEROUS with SSR!
export const globalUser = $state({ name: null, id: null })
<!-- +page.svelte -->
<script>
	import { globalUser } from '$lib/stores/global.svelte'

	let { data } = $props()

	// Mutating global state during render
	globalUser.name = data.user.name // ❌ May leak to other requests!
</script>

On the server, a single Node.js process handles multiple requests. Global state persists between requests.

The Solution

Use context for request-scoped data:

<!-- +layout.svelte -->
<script>
	import { setContext } from 'svelte'

	let { data } = $props()

	// Create fresh context for each request
	// Context is automatically isolated per-component-tree
	setContext('user', data.user)
</script>

<slot />

Context is tied to a specific component tree. Each request gets its own component tree, so context is naturally isolated.


Pitfall 10: Context in Tight Loops

Accessing context repeatedly in loops or frequently-called functions.

The Symptom

Sluggish UI performance when many components consume the same context, or when context-dependent functions are called frequently.

The Mistake

<script>
	import { getContext } from 'svelte'

	let { items } = $props()

	// ❌ Getting context inside a function called per item
	function formatItem(item) {
		const settings = getContext('settings') // Called many times!
		return `${item.name} (${settings.currency})`
	}
</script>

{#each items as item}
	<p>{formatItem(item)}</p>
{/each}

While getContext is fast, calling it repeatedly in loops adds unnecessary overhead. More importantly, this pattern suggests a misunderstanding of how context works—you only need to get it once.

The Solution

Get context once at the top level of your script:

<script>
	import { getContext } from 'svelte'

	let { items } = $props()

	// ✅ Get context once during initialization
	const settings = getContext('settings')

	function formatItem(item) {
		return `${item.name} (${settings.currency})`
	}
</script>

{#each items as item (item.id)}
	<p>{formatItem(item)}</p>
{/each}

The context value doesn’t change during the component’s lifecycle (though its properties might if it’s reactive). Capture the reference once and reuse it throughout your component.

Don’t access context inside $derived calculations that run frequently:

<script>
	import { getContext } from 'svelte'

	let { price } = $props()

	// ❌ Context access inside derived (works but unclear intent)
	let formatted = $derived(() => {
		const settings = getContext('settings')
		return new Intl.NumberFormat(settings.locale, {
			style: 'currency',
			currency: settings.currency
		}).format(price)
	})
</script>
<script>
	import { getContext } from 'svelte'

	let { price } = $props()

	// ✅ Get context at top level, use in derived
	const settings = getContext('settings')

	let formatted = $derived(
		new Intl.NumberFormat(settings.locale, {
			style: 'currency',
			currency: settings.currency
		}).format(price)
	)
</script>

Both work, but the second version makes the data flow clearer and follows the established pattern of capturing context during initialization.


Debugging Techniques

When context isn’t working, these techniques help diagnose the issue.

Technique 1: Context Inspector Component

Create a component that displays all available context:

<!-- ContextDebugger.svelte -->
<script>
	import { getAllContexts } from 'svelte'

	let { show = true } = $props()

	const contexts = getAllContexts()

	function formatValue(value) {
		if (value === undefined) return 'undefined'
		if (value === null) return 'null'
		if (typeof value === 'function') return '[Function]'
		try {
			return JSON.stringify(value, null, 2)
		} catch {
			return String(value)
		}
	}

	function formatKey(key) {
		if (typeof key === 'symbol') {
			return key.toString()
		}
		return String(key)
	}
</script>

{#if show && import.meta.env.DEV}
	<details class="context-debugger" open>
		<summary>🔍 Context ({contexts.size} keys)</summary>

		{#if contexts.size === 0}
			<p class="empty">No context available at this position</p>
		{:else}
			<ul>
				{#each [...contexts.entries()] as [key, value] (key)}
					<li>
						<strong class="key">{formatKey(key)}</strong>
						<pre class="value">{formatValue(value)}</pre>
					</li>
				{/each}
			</ul>
		{/if}
	</details>
{/if}

<style>
	.context-debugger {
		position: fixed;
		bottom: 1rem;
		right: 1rem;
		background: #1e1e2e;
		color: #cdd6f4;
		padding: 0.75rem;
		border-radius: 8px;
		font-family: 'Fira Code', monospace;
		font-size: 12px;
		max-width: 400px;
		max-height: 400px;
		overflow: auto;
		z-index: 9999;
		box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
	}

	summary {
		cursor: pointer;
		font-weight: bold;
		padding: 0.25rem 0;
	}

	ul {
		list-style: none;
		padding: 0;
		margin: 0.5rem 0 0;
	}

	li {
		padding: 0.5rem;
		border-bottom: 1px solid #313244;
	}

	li:last-child {
		border-bottom: none;
	}

	.key {
		color: #89b4fa;
	}

	.value {
		margin: 0.25rem 0 0;
		padding: 0.5rem;
		background: #313244;
		border-radius: 4px;
		white-space: pre-wrap;
		word-break: break-all;
		color: #a6e3a1;
	}

	.empty {
		color: #f38ba8;
		font-style: italic;
	}
</style>

Drop this anywhere to see what context is available:

<!-- SomeDeeplyNestedComponent.svelte -->
<script>
	import ContextDebugger from '$lib/debug/ContextDebugger.svelte'
</script>

<div>
	<!-- Component content -->
</div>

<ContextDebugger />

Technique 2: Console Logging in Providers

Add development logging to trace context setup:

<!-- AuthProvider.svelte -->
<script>
	import { setContext } from 'svelte'

	let { children } = $props()

	let user = $state(null)

	const context = {
		get user() {
			return user
		},
		get isAuthenticated() {
			return user !== null
		}
	}

	setContext('auth', context)

	// Development logging
	if (import.meta.env.DEV) {
		console.log('🔐 AuthProvider: Context set', context)

		$effect(() => {
			console.log('🔐 AuthProvider: User changed', user)
		})
	}
</script>

{@render children()}

Technique 3: Provider Stack Tracking

Track the provider hierarchy:

<!-- Add to each provider -->
<script>
	import { setContext, getContext, hasContext } from 'svelte'

	// Track provider stack
	const parentStack = hasContext('__debug_providers') ? getContext('__debug_providers') : []

	const myStack = [...parentStack, 'AuthProvider']
	setContext('__debug_providers', myStack)

	if (import.meta.env.DEV) {
		console.log('Provider stack:', myStack.join(''))
	}
</script>

Output:

Provider stack: ThemeProvider → AuthProvider
Provider stack: ThemeProvider → AuthProvider → CartProvider

Technique 4: Checking Consumer Position

Verify where in the tree a consumer is:

<script>
	import { getContext, hasContext, getAllContexts } from 'svelte'

	const available = getAllContexts()
	const hasAuth = hasContext('auth')
	const hasTheme = hasContext('theme')

	if (import.meta.env.DEV) {
		console.group('Component: UserProfile')
		console.log('Available context keys:', [...available.keys()])
		console.log('Has auth context:', hasAuth)
		console.log('Has theme context:', hasTheme)
		console.groupEnd()
	}
</script>

Technique 5: Error Boundary Wrapping

Catch context errors gracefully:

<!-- SafeContextConsumer.svelte -->
<script>
	import { getContext, hasContext } from 'svelte'

	let { contextKey, fallback, children } = $props()

	let error = null
	let context = null

	try {
		if (!hasContext(contextKey)) {
			throw new Error(`Context '${contextKey}' not found`)
		}
		context = getContext(contextKey)
	} catch (e) {
		error = e.message
		if (import.meta.env.DEV) {
			console.error(`Context error: ${e.message}`)
		}
	}
</script>

{#if error}
	{#if fallback}
		{@render fallback(error)}
	{:else}
		<div class="context-error">
			Context Error: {error}
		</div>
	{/if}
{:else}
	{@render children(context)}
{/if}

Quick Reference: Symptoms and Solutions

SymptomLikely CauseSolution
“can only be called during initialization”Context function in async codeCall at top level, capture value
Context returns undefinedMissing providerAdd provider as ancestor
Context returns undefinedWrong keyUse shared keys, Symbols, or createContext
Context returns undefinedProvider is sibling not ancestorRestructure to wrap consumer
Values don’t updatePlain object, not $stateUse $state for reactive values
Values stop updating after certain actionReassigned state objectMutate properties, don’t reassign
Destructured values don’t updateCaptured primitive valuesAccess via object or use $derived
Modal/portal can’t access contextMounted outside treeForward context with getAllContexts
SSR data leaking between usersMutating global stateUse context for request-scoped data
Sluggish performance with many consumersContext in loops/frequent callsGet context once at top level

Conclusion

Context pitfalls aren’t random bugs—they’re predictable consequences of how JavaScript handles values, how Svelte manages reactivity, and how component trees establish hierarchical relationships. Once you understand these underlying mechanisms, the pitfalls become intuitive rather than mysterious.

The timing constraint exists because context is structural. The reassignment problem exists because JavaScript passes object references, not the variables themselves. The destructuring trap exists because primitives are copied by value. The provider positioning requirement exists because context flows downward through the tree. Each pitfall has a logical cause rooted in fundamental language and framework behaviors.

The debugging techniques in this article—context inspectors, provider stack tracking, strategic logging—transform context from a black box into a transparent system you can observe and reason about. When something goes wrong, you can systematically identify whether it’s a timing issue, a reference issue, a tree structure issue, or a key mismatch.

Most importantly, these pitfalls become increasingly rare as you internalize the patterns. Call context functions at the top level. Mutate properties, don’t reassign objects. Access through the object, don’t destructure primitives. Wrap consumers in providers. Use shared keys or Symbols. Follow these rules consistently, and context becomes a reliable, predictable tool.


Key Takeaways

Context pitfalls generally fall into a few categories:

  1. Timing issues — Context functions must be called during component initialization. Capture values early, use them later.

  2. Reference issues — Don’t reassign state objects; mutate properties. Don’t destructure primitives; access via object.

  3. Tree structure issues — Providers must be ancestors, not siblings. Dynamic components need context forwarded explicitly.

  4. Key mismatches — Use shared constants, Symbols, or createContext to avoid typos.

  5. Reactivity issues — Use $state for reactive values. Plain objects aren’t tracked.

  6. SSR issues — Don’t mutate global state. Use context for request-scoped data.

When debugging, use getAllContexts() to see what’s available, add logging to providers, and check that providers are ancestors of consumers. With these patterns recognized, you’ll quickly diagnose and fix context issues when they arise.


What’s Next

Understand when context is the right choice in Context vs Other Patterns, comparing context to stores, props, events, and building a decision framework.


See Also

Official Documentation