---
title: Quickstart
description: Install Shaders, render your first effect, and drive a prop from state.
icon: rocket
category: code
---

# Quickstart

By the end of this page you'll have a gradient with a cursor trail running in your app, sized with CSS and driven by state. Pick your framework in any code block and every block follows; plain JavaScript works too.

## Installing the package

Shaders is one package for every framework:

```bash
npm install shaders
```

Import the components from the entry point for your framework: `shaders/vue`, `shaders/react`, `shaders/svelte` or `shaders/solid`. Without a framework, the JavaScript API comes from `shaders/js`.

## Rendering your first effect

Let's start with a gradient and put a cursor trail on top of it. Add this component anywhere in your app:

```vue-html
<script setup>
import { Shader, LinearGradient, CursorTrail } from 'shaders/vue'
</script>

<template>
  <Shader class="w-full h-64">
    <LinearGradient color-a="#0f172a" color-b="#7c3aed" />
    <CursorTrail />
  </Shader>
</template>
```

```jsx
import { Shader, LinearGradient, CursorTrail } from 'shaders/react'

export default function Hero() {
  return (
    <Shader className="w-full h-64">
      <LinearGradient colorA="#0f172a" colorB="#7c3aed" />
      <CursorTrail />
    </Shader>
  )
}
```

```tsx
<script>
  import { Shader, LinearGradient, CursorTrail } from 'shaders/svelte'
</script>

<Shader class="w-full h-64">
  <LinearGradient colorA="#0f172a" colorB="#7c3aed" />
  <CursorTrail />
</Shader>
```

```tsx
import { Shader, LinearGradient, CursorTrail } from 'shaders/solid'

export default function Hero() {
  return (
    <Shader class="w-full h-64">
      <LinearGradient colorA="#0f172a" colorB="#7c3aed" />
      <CursorTrail />
    </Shader>
  )
}
```

```javascript
// <canvas id="hero" class="w-full h-64"></canvas>
import { createShader } from 'shaders/js'

const shader = await createShader(document.getElementById('hero'), {
  components: [
    { type: 'LinearGradient', props: { colorA: '#0f172a', colorB: '#7c3aed' } },
    { type: 'CursorTrail', props: {} },
  ],
})
```

Move your cursor over the canvas to see the trail.

`<Shader>` renders a single `<canvas>`. The components inside it are **layers**, drawn in order like layers in Figma: the gradient first, then the trail on top. In plain JavaScript you bring the `<canvas>`, and each entry in `components` is a layer.

## Sizing the canvas

The canvas has no size of its own. It takes whatever size you give `<Shader>` through `class` (`className` in React) or `style`:

```vue-html
<!-- Fill the viewport -->
<Shader class="w-screen h-screen">
  <LinearGradient />
</Shader>

<!-- Fixed size -->
<Shader style="width: 600px; height: 400px">
  <LinearGradient />
</Shader>

<!-- Full width, 16:9 -->
<Shader class="w-full aspect-video">
  <LinearGradient />
</Shader>
```

```jsx
{/* Fill the viewport */}
<Shader className="w-screen h-screen">
  <LinearGradient />
</Shader>

{/* Fixed size */}
<Shader style={{ width: 600, height: 400 }}>
  <LinearGradient />
</Shader>

{/* Full width, 16:9 */}
<Shader className="w-full aspect-video">
  <LinearGradient />
</Shader>
```

```tsx
<!-- Fill the viewport -->
<Shader class="w-screen h-screen">
  <LinearGradient />
</Shader>

<!-- Fixed size -->
<Shader style="width: 600px; height: 400px">
  <LinearGradient />
</Shader>

<!-- Full width, 16:9 -->
<Shader class="w-full aspect-video">
  <LinearGradient />
</Shader>
```

```tsx
{/* Fill the viewport */}
<Shader class="w-screen h-screen">
  <LinearGradient />
</Shader>

{/* Fixed size */}
<Shader style={{ width: '600px', height: '400px' }}>
  <LinearGradient />
</Shader>

{/* Full width, 16:9 */}
<Shader class="w-full aspect-video">
  <LinearGradient />
</Shader>
```

```html
<!-- Fill the viewport -->
<canvas id="hero" class="w-screen h-screen"></canvas>

<!-- Fixed size -->
<canvas id="hero" style="width: 600px; height: 400px"></canvas>

<!-- Full width, 16:9 -->
<canvas id="hero" class="w-full aspect-video"></canvas>
```

The canvas resizes itself when its CSS size changes, so you don't need to call anything on resize. Style `<Shader>`, not the `<canvas>` inside it: the internal markup can change between releases. In plain JavaScript, call `shader.resize()` if you change the canvas size from script.

## Setting props

Each component takes props the same way any component in your framework does. In Vue, attributes are kebab-case and non-string values need the `:` binding; in plain JavaScript they go in each component's `props` object:

```vue-html
<Shader class="w-full h-64">
  <LinearGradient
    color-a="#ff6b6b"
    color-b="#4ecdc4"
    :angle="45"
    color-space="oklch"
  />
</Shader>
```

```jsx
<Shader className="w-full h-64">
  <LinearGradient
    colorA="#ff6b6b"
    colorB="#4ecdc4"
    angle={45}
    colorSpace="oklch"
  />
</Shader>
```

```tsx
<Shader class="w-full h-64">
  <LinearGradient
    colorA="#ff6b6b"
    colorB="#4ecdc4"
    angle={45}
    colorSpace="oklch"
  />
</Shader>
```

```tsx
<Shader class="w-full h-64">
  <LinearGradient
    colorA="#ff6b6b"
    colorB="#4ecdc4"
    angle={45}
    colorSpace="oklch"
  />
</Shader>
```

```javascript
const canvas = document.getElementById('hero')

const shader = await createShader(canvas, {
  components: [
    {
      type: 'LinearGradient',
      props: {
        colorA: '#ff6b6b',
        colorB: '#4ecdc4',
        angle: 45,
        colorSpace: 'oklch',
      },
    },
  ],
})
```

`colorSpace="oklch"` blends the two colors through a perceptual color space, so the middle of the gradient stays bright instead of going gray. Every component's props, with defaults and ranges, are in the [component docs](/docs/components), and the design editor exports any effect you build as code.

## Driving a prop from state

Props accept your framework's state like any other prop. In plain JavaScript, give the component an `id` and pass new props to `shader.update()`. We'll wire the gradient's angle to a range input:

```vue-html
<script setup>
import { ref } from 'vue'
import { Shader, LinearGradient } from 'shaders/vue'

const angle = ref(0)
</script>

<template>
  <Shader class="w-full h-64">
    <LinearGradient color-a="#ff6b6b" color-b="#4ecdc4" :angle="angle" />
  </Shader>

  <input v-model.number="angle" type="range" min="0" max="360" />
</template>
```

```jsx
import { useState } from 'react'
import { Shader, LinearGradient } from 'shaders/react'

export default function GradientDial() {
  const [angle, setAngle] = useState(0)

  return (
    <>
      <Shader className="w-full h-64">
        <LinearGradient colorA="#ff6b6b" colorB="#4ecdc4" angle={angle} />
      </Shader>

      <input
        type="range"
        min={0}
        max={360}
        value={angle}
        onChange={e => setAngle(Number(e.target.value))}
      />
    </>
  )
}
```

```tsx
<script>
  import { Shader, LinearGradient } from 'shaders/svelte'

  let angle = $state(0)
</script>

<Shader class="w-full h-64">
  <LinearGradient colorA="#ff6b6b" colorB="#4ecdc4" {angle} />
</Shader>

<input type="range" min="0" max="360" bind:value={angle} />
```

```tsx
import { createSignal } from 'solid-js'
import { Shader, LinearGradient } from 'shaders/solid'

export default function GradientDial() {
  const [angle, setAngle] = createSignal(0)

  return (
    <>
      <Shader class="w-full h-64">
        <LinearGradient colorA="#ff6b6b" colorB="#4ecdc4" angle={angle()} />
      </Shader>

      <input
        type="range"
        min={0}
        max={360}
        value={angle()}
        onInput={e => setAngle(Number(e.currentTarget.value))}
      />
    </>
  )
}
```

```javascript
// <input id="angle" type="range" min="0" max="360" value="0" />
const canvas = document.getElementById('hero')

const shader = await createShader(canvas, {
  components: [
    { type: 'LinearGradient', id: 'gradient', props: { colorA: '#ff6b6b', colorB: '#4ecdc4', angle: 0 } },
  ],
})

document.getElementById('angle').addEventListener('input', e => {
  shader.update('gradient', { angle: Number(e.target.value) })
})
```

Drag the slider and the gradient turns on the next frame. Changing `angle` updates a value on the GPU; the effect doesn't recompile.

## Cleaning up

The framework components clean up after themselves: when `<Shader>` unmounts, rendering stops and its GPU memory is freed. In plain JavaScript, call `destroy()` when the canvas leaves the page, for example on a route change in a single-page app:

```javascript
shader.destroy()
```

## Using server rendering

Shaders needs a browser with a GPU, so it can't render on the server. Each meta-framework handles this differently:

- **Next.js**: put `'use client'` at the top of any file that renders a `<Shader>`. See [Using Next.js](/docs/guide/ssr#using-nextjs).
- **Nuxt**: wrap the `<Shader>` in `<ClientOnly>`. See [Using Nuxt](/docs/guide/ssr#using-nuxt).
- **SvelteKit**: nothing to do, because `<Shader>` renders nothing on the server and starts on the client. See [Using SvelteKit](/docs/guide/ssr#using-sveltekit).
- **SolidStart**: load the component that renders a `<Shader>` with `clientOnly`. See [Using SolidStart](/docs/guide/ssr#using-solidstart).

## Next steps

- [Composing effects](/docs/guide/composing-effects): stack and nest components, blend and mask layers.
- [Layout and positioning](/docs/guide/layout-positioning): place the canvas behind or around your UI.
- [Props and reactivity](/docs/guide/props-reactivity): bind state to props and animate them.
- [Component docs](/docs/components): every component with a live preview and its props.