Arrangement
Transport and mixer
EQ and filters
Modulation
Utility
Instruments
Audio UI
Plays or shows notes, for a virtual instrument, a MIDI visualizer or a chord reference. The registry component wraps the Piano primitive from @audio-ui/react and draws white and black keys for any MIDI range.
Overview
- Range:
minPitchandmaxPitchare MIDI pitches (default48and72, C3 to C5), orpitcheslists the exact keys to show. Key colors and layout follow the chromatic pattern. - Events:
onNoteOn(pitch, eventDetails)andonNoteOff(pitch, eventDetails)report MIDI pitches, ready for an audio engine.eventDetails.reasonsays how the note started ("press","glide"or"keyboard"), andeventDetails.cancel()inonNoteOnkeeps the key silent. - Pointer: press and hold a key to hold its note. Drag across keys for a glissando: each key plays as the pointer crosses it. Each touch holds its own key, so chords work on a touch screen.
- External notes:
activeNoteslights keys from outside, such as a MIDI input or a sequencer's current chord, without callingonNoteOnoronNoteOff. - Accessibility: each key is a button named after its pitch ("C4"), behind one tab stop. The arrow keys move between keys, and Space or Enter holds the focused key until it is released.
- Layout: the primitive can also lay keys out vertically (
orientation="vertical", lowest key at the bottom) or in equal widths (layout="even"). The piano roll's keyboard uses this.
Installation
pnpm dlx shadcn@latest add @audio-ui/piano
Anatomy
Import the primitive and assemble its parts.
import { Piano } from "@audio-ui/react";
<Piano.Root>
<Piano.Keys>
{(pitch) => (
<Piano.Key pitch={pitch}>
<Piano.KeyLabel />
</Piano.Key>
)}
</Piano.Keys>
</Piano.Root>Usage
import { Piano } from "@/components/audio-ui/piano";Note events:
<Piano
maxPitch={72}
minPitch={48}
onNoteOff={(pitch) => synth.release(pitch)}
onNoteOn={(pitch) => synth.trigger(pitch)}
/>External notes (MIDI input, a sequencer's current chord):
<Piano activeNotes={midiInputNotes} maxPitch={72} minPitch={48} />A MIDI keyboard: useMidiInput listens to the page's MIDI input. Browsers ask for permission, so connect it from a click: with requestAccess, or the ready-made button, npx shadcn@latest add @audio-ui/midi-connect-control.
import { useMidiInput } from "@/hooks/use-midi-input";
const midi = useMidiInput({
onNoteOff: (pitch) => synth.release(pitch),
onNoteOn: (pitch, velocity) => synth.trigger(pitch, velocity),
});
<button onClick={midi.requestAccess} type="button">Connect MIDI</button>
<Piano activeNotes={midi.activeNotes} maxPitch={72} minPitch={48} />Everything useMidiInput gives is in Input.
Interaction
Pointer
Behavior · 4
onNoteOn; the key stays pressed while held.onNoteOff.onNoteOff) and the next one sounds (onNoteOn) as the pointer crosses it.Keyboard
The keys share one tab stop. On the focused key:
Keys · 4
onNoteOn on press, onNoteOff on releaseAPI reference
Root
A keyboard to play: every key sounds while pressed, by pointer or from the keyboard. Dragging across the keys glides from one to the next, and each pointer holds its own key, so chords can be played on a touch screen. It is laid out like a piano, or with layout="even" as one equal share per key, the way a piano roll's rows are; orientation="vertical" puts the low end at the bottom. The keys are one tab stop, walked with the arrow keys. Renders a <div> element.
Props · 9
eventDetails.cancel() keeps it silent.minPitch…maxPitch.Data attributes · 1
data-disabledKeys
Renders children once per key, lowest first: the place to put a Piano.Key for each pitch. Renders no element of its own.
Props · 1
Key
One key, placed along the keyboard for its pitch. It sounds while pressed: by pointer through the keyboard, or with Enter or Space while focused. Its accessible name defaults to the pitch, such as "C4". Renders a <button> element.
Props · 1
Data attributes · 2
data-activeactiveNotes.data-disabledKeyLabel
The name of the key it sits in, such as "C4". Hidden from assistive technology, which reads the key's own name. Renders a <span> element.
Data attributes · 1
data-disabledRegistry component
Piano accepts style props (size, className) plus primitive props from PianoPrimitive.Root.Props.
Value and state
Props · 9
minPitch…maxPitch.eventDetails.cancel() keeps it silent.Layout and display
Props · 2
Additional valid HTML attributes passed to Piano.Root are forwarded.
Examples
External notes via activeNotes
Audio UI
More Shadcn Piano components
Browse 2 production-ready audio/ui Piano components for players, mixers, and synth interfaces. These examples use the headless primitives from @audio-ui/react and stay fully compatible with Shadcn Create so radius, color, and typography match your configured theme.
Browse all 2 Shadcn Piano components for copy-ready layouts, dashboards, and forms built with Tailwind CSS in the audio/ui library.