SSR with SvelteKit / Svelte
Shaders uses WebGPU, which requires a browser environment. While the Svelte package is built to be SSR-aware, the underlying WebGPU renderer requires a browser context to initialize. This page covers how to handle this correctly in SvelteKit.
The default behavior
Shaders for Svelte is built to be SSR-safe by design. When the <Shader> component is server-rendered, it renders nothing — no canvas element, no error. The GPU initialization only happens client-side on mount.
In many cases, you don't need any special handling:
<script>
import { Shader, LinearGradient } from 'shaders/svelte'
</script>
<Shader class="w-full h-64">
<LinearGradient />
</Shader>
This works in SvelteKit out of the box. The component renders empty on the server and initializes on the client.
onMount for browser-only code
If you have component-level code that depends on the shader being initialized (for example, you need to access the canvas element), wrap it in onMount:
<script>
import { onMount } from 'svelte'
import { Shader, LinearGradient } from 'shaders/svelte'
let shaderEl
onMount(() => {
// Safe to interact with the DOM and canvas here
console.log(shaderEl)
})
</script>
<Shader bind:this={shaderEl} class="w-full h-64">
<LinearGradient />
</Shader>
Conditional rendering with browser
For more explicit control over what renders on the server vs. client, use SvelteKit's $app/environment:
<script>
import { browser } from '$app/environment'
import { Shader, LinearGradient } from 'shaders/svelte'
</script>
{#if browser}
<Shader class="w-full h-64">
<LinearGradient />
</Shader>
{:else}
<!-- Optional server-rendered placeholder -->
<div class="w-full h-64 bg-gray-900 rounded" />
{/if}
This gives you explicit control over the placeholder shown during SSR, which can help avoid layout shift.
Lazy loading
To defer loading the Shaders bundle entirely until client-side:
<script>
import { onMount } from 'svelte'
let ShaderComponent
let LinearGradientComponent
onMount(async () => {
const { Shader, LinearGradient } = await import('shaders/svelte')
ShaderComponent = Shader
LinearGradientComponent = LinearGradient
})
</script>
{#if ShaderComponent}
<svelte:component this={ShaderComponent} class="w-full h-64">
<svelte:component this={LinearGradientComponent} />
</svelte:component>
{/if}