Dial & Encoder Support
Build encoder-aware actions with dial rendering, rotation handlers, and trigger hints.
Stream Deck+ encoder actions combine three capabilities:
- Dial rendering on the encoder display.
- Rotation and press events through the event hooks.
- Touch taps through
useTouchTap().
Dial Rendering
The dial component defined in defineAction renders to the encoder display. The library pushes the image with action.setFeedback().
function VolumeDial() {
const [volume, setVolume] = useState(50);
const [muted, setMuted] = useState(false);
useDialRotate(({ ticks }) => {
if (!muted) {
setVolume((v) => Math.max(0, Math.min(100, v + ticks * 2)));
}
});
useDialDown(() => {
setMuted((m) => !m);
});
useDialHint({
rotate: "Adjust volume",
press: muted ? "Unmute" : "Mute",
});
return (
<div
className={cn(
"flex flex-col items-center justify-center w-full h-full gap-1",
muted ? "bg-[#4a0000]" : "bg-[#1a1a1a]",
)}
>
<span className="text-[#888] text-[12px]">Volume</span>
<span className={cn("text-[24px] font-bold", muted ? "text-[#ff4444]" : "text-white")}>
{muted ? "MUTE" : `${volume}%`}
</span>
{!muted && (
<ProgressBar value={volume} height={4} color="#4CAF50" background="#333" borderRadius={2} />
)}
</div>
);
}
export const volumeAction = defineAction<VolumeSettings>({
uuid: "com.example.plugin.volume",
dial: VolumeDial,
defaultSettings: { volume: 50, muted: false },
info: {
name: "Volume",
icon: "imgs/actions/volume",
encoder: {
layout: "$A0",
triggerDescription: {
rotate: "Adjust volume",
push: "Toggle mute",
},
},
},
});Trigger Descriptions
Stream Deck+ shows contextual hints for what each dial action does. Use useDialHint to set these:
useDialHint({
rotate: "Adjust volume",
press: "Toggle mute",
touch: "Open settings",
longTouch: "Reset to default",
});The hints update automatically when the values change. Under the hood this calls action.setTriggerDescription().
Touch Interaction
Use useTouchTap() when your encoder action needs touch input from the Stream Deck+ touch area:
function VolumeDial() {
useTouchTap(({ tapPos, hold }) => {
console.log("touch", tapPos, hold);
});
return <div className="w-full h-full" />;
}Key + Dial Actions
An action can support both key and encoder placement. Define separate components:
export const volumeAction = defineAction<VolumeSettings>({
uuid: "com.example.plugin.volume",
key: VolumeKey, // Rendered when placed on a key
dial: VolumeDial, // Rendered when placed on an encoder
defaultSettings: { volume: 50, muted: false },
info: {
name: "Volume",
icon: "imgs/actions/volume",
encoder: {
layout: "$A0",
triggerDescription: {
rotate: "Adjust volume",
push: "Toggle mute",
},
},
},
});If dial is not provided, the key component is used as a fallback for encoder placement.