Installation
Install @fcannizzaro/streamdeck-react, React, and the tooling needed to bundle a Stream Deck plugin.
Requirements
- Node.js 20+
- React 19+
- TypeScript 6+ if your plugin is written in TypeScript
Install the Library
bun add @fcannizzaro/streamdeck-react react@fcannizzaro/streamdeck-react ships the Stream Deck SDK integration and Takumi renderer bindings as package dependencies, so a basic plugin does not need extra runtime packages just to render keys.
Start with the Create CLI
If you want a ready-to-run plugin scaffold, use create-streamdeck-react:
bun create streamdeck-reactThe CLI asks for your plugin UUID, author, supported platforms, native targets, and starter example, then generates a complete .sdPlugin project.
Add the Bundler
Use Vite 8+ with Rolldown:
bun add -d vite@8.0.0 @vitejs/plugin-react@6.0.1If you want to use React Compiler, also add:
bun add -d @rolldown/plugin-babel @babel/core babel-plugin-react-compilerNative Bindings
Native Takumi .node binaries are lazy-loaded by default. On first plugin startup, the binary matching the current platform is downloaded from npm and cached on disk. No manual installation of platform-specific packages is needed.
If you prefer to bundle binaries at build time (e.g. for offline environments), set nativeBindings: "copy" on streamDeckReact() and install the matching @takumi-rs/core-* packages:
| Target | Package |
|---|---|
darwin-arm64 | @takumi-rs/core-darwin-arm64 |
darwin-x64 | @takumi-rs/core-darwin-x64 |
win32-arm64 | @takumi-rs/core-win32-arm64-msvc |
win32-x64 | @takumi-rs/core-win32-x64-msvc |
Example (copy mode only):
bun add @takumi-rs/core-darwin-arm64Custom Native Modules
If your plugin depends on additional NAPI-RS (or other native .node) packages, you can register them with the same lazy-load or copy infrastructure via the nativeModules option:
streamDeckReact({
manifest: {
/* ... */
},
nativeModules: [
{
importSpecifier: "@mypkg/native-core",
scope: "@mypkg", // optional — auto-inferred from scoped packages
bindings: {
"darwin-arm64": { pkg: "native-core-darwin-arm64", file: "native.darwin-arm64.node" },
"darwin-x64": { pkg: "native-core-darwin-x64", file: "native.darwin-x64.node" },
"win32-x64": { pkg: "native-core-win32-x64", file: "native.win32-x64-msvc.node" },
},
exports: ["MyClass", "helperFn"],
},
],
});Each entry gets the same treatment as the built-in Takumi binding:
- Lazy mode (default): a virtual module downloads the
.nodefile from npm at runtime. - Copy mode:
.nodefiles are copied fromnode_modulesduring the build.
The nativeBindings option controls the strategy for all native modules. See the NativeModuleConfig type for the full interface.
What Gets Installed
react-reconcilerpowers the custom React renderer.@elgato/streamdeckhandles the Stream Deck SDK connection.@takumi-rs/coreand@takumi-rs/helpersrender your JSX tree to hardware-ready images.
Fonts
You must provide at least one font to createPlugin(). The easiest approach is googleFont() which downloads TTF fonts directly from Google Fonts and caches them to disk:
import { createPlugin, googleFont } from "@fcannizzaro/streamdeck-react";
const inter = await googleFont("Inter");
const plugin = createPlugin({
fonts: [inter],
actions: [...],
});For fonts not available on Google Fonts, load them manually via readFile:
import { readFile } from "node:fs/promises";
const font = {
name: "MyFont",
data: await readFile(new URL("../fonts/MyFont-Regular.ttf", import.meta.url)),
weight: 400,
style: "normal" as const,
};See Font Management for the full API and caching details.