Copy-paste installation, the skill file, and the shared systems every component plugs into.
Sable UI is a copy-paste library. Instead of importing components from a versioned package, you copy their TypeScript source into your project, so the code is yours to read, edit, and own. Installation runs through the shadcn CLI, which copies the source files, copies any shared internal utilities, pulls in the components an effect builds on, and installs the npm dependencies it needs.
npx shadcn@latest add @sable/liquid-mediaEvery component page offers the same three paths, so you can pick whichever fits how you work:
npx shadcn@latest add @sable/<name> yourself in a terminal.All three paths write the same files and the same skill file to disk.
Every install also writes a SKILL.md to both .claude/skills/sable-ui/ and .agents/skills/sable-ui/, so coding agents such as Claude Code, Codex, Cursor, GitHub Copilot, and Gemini CLI discover it automatically.
It carries the wiring the source alone cannot express: exactly one <WebglProvider> for the whole page, exactly one <SmoothScroll>, which React Compiler lint rules to disable on the copied files, and where each piece belongs. An agent that installs a component later picks the skill up and follows it without any extra setup.
Most UI libraries give you isolated components. Sable's WebGL effects instead plug into systems that are mounted once at the app root, which is what lets many effects coexist on one scrolling page.
Browsers cap how many live WebGL contexts can exist at once, and contexts cannot share GPU resources. A library that spins up one <canvas> per effect breaks down as soon as you want several on a page.
WebglProvider solves this. Mount it once around your app root: it owns a single fullscreen fixed canvas, the global camera, the pointer event source, and one post-processing composer. Every WebGL component teleports its meshes into that shared canvas from anywhere in the DOM tree, so adding an effect never adds a context.
import { WebglProvider } from "@/components/webgl-provider/webgl-provider"
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<WebglProvider>{children}</WebglProvider>
</body>
</html>
)
}Scroll-driven effects need the scroll position to agree with what the canvas draws. SmoothScroll runs Lenis on Motion's frame loop, so scrolling, animation, and the WebGL canvas all update on the same clock. Mount it once around the app root; scroll components install it automatically.
<SmoothScroll>
<WebglProvider>{children}</WebglProvider>
</SmoothScroll>WebGL effects paint on top of the real DOM element they replace — an <img>, a <video>, or text — never in place of it. The element stays in the document, hidden with opacity: 0 (never display: none), so it keeps its layout, still receives pointer events, and remains crawlable and readable by screen readers.
That is what keeps a page full of shaders SEO-friendly and accessible: the content lives in the DOM, the effect is painted over it, and each plane tracks its element as the page scrolls and resizes.
An add command copies three kinds of files:
components/<name>/<name>.tsx, imported as @/components/<name>/<name>.hooks/ and lib/, outside components/. They are skipped when they already exist, so several components share one copy.webgl-image — install alongside it automatically.From there the component is plain source in your project: edit it, restyle it, or strip it down however you like.