Keys
A responsive and interactive piano keyboard component with configurable number of keys, note highlighting, and multiple styling modes.
The Keys component provides a responsive and interactive piano keyboard visualization. It shares the same layout and accessibility as other vector components; see the Vector introduction.
- Flexible configuration: Variable number of keys (1-128), different starting positions, octave transposition
- Note highlighting: Visual feedback for active notes via
notesOnprop - Multiple styling modes: theme-based, classic ivory/ebony, and inverted classic
- Interactive control: Optional
onChangecallback for key press/release events - Full theming support: color, roundness customization (in theme mode)
Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
nbKeys | number | 61 | Optional | Number of keys on the keyboard (1-128) |
startKey | "A" | "B" | "C" | "D" | "E" | "F" | "G" | — | Optional | Starting note name (A-G). Default 'C' for 61 keys, 'A' for 88 keys |
octaveShift | number | 0 | Optional | Octave transpose index. Positive values shift notes up by that many octaves, negative values shift down |
notesOn | (string | number)[] | [] | Optional | Array of notes that should be highlighted. Notes can be specified as strings (e.g., 'C4', 'F#5') or MIDI note numbers (e.g., 60 for C4) |
keyStyle | "theme" | "classic" | "classic-inverted" | theme | Optional | Key styling mode. 'theme' uses theme colors, 'classic' uses ivory/ebony colors, 'classic-inverted' inverts the classic colors |
onChange | (event: AudioControlEvent<{ note: number; active: boolean }>) => void | — | Optional | Callback triggered when a key is pressed or released. Only active if this prop is provided. |
color | string | — | Optional | Component primary color - any valid CSS color value (used in theme mode) |
roundness | number | — | Optional | Roundness for component corners (normalized 0.0-1.0) |
adaptiveSize | boolean | false | Optional | Whether the component stretches to fill its container |
size | "xsmall" | "small" | "normal" | "large" | "xlarge" | normal | Optional | Size of the component |
displayMode | "scaleToFit" | "fill" | scaleToFit | Optional | Layout mode for the control |
onClick | React.MouseEventHandler | — | Optional | Click event handler |
className | string | — | Optional | Additional CSS classes |
style | React.CSSProperties | — | Optional | Additional inline styles |
Basic Usage
The Keys component can be used as a simple visualization or as an interactive control:
import { Keys } from "@cutoff/audio-ui-react";
export default function BasicKeys() {
return <Keys size="large" />;
}Number of Keys
Configure the number of keys on the keyboard (1-128):
import { Keys } from "@cutoff/audio-ui-react";
export default function NumberOfKeys() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys nbKeys={25} label="25 Keys" size="large" />
<Keys nbKeys={61} label="61 Keys (Default)" size="large" />
<Keys nbKeys={88} label="88 Keys (Full Piano)" size="large" />
</div>
);
}Starting Position
Control which note the keyboard starts with:
import { Keys } from "@cutoff/audio-ui-react";
export default function StartingPosition() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys nbKeys={25} startKey="C" label="Starting at C" size="large" />
<Keys nbKeys={25} startKey="A" label="Starting at A" size="large" />
<Keys nbKeys={25} startKey="F" label="Starting at F" size="large" />
</div>
);
}Octave Transposition
Use the octaveShift prop to transpose the keyboard up or down by octaves:
import { Keys } from "@cutoff/audio-ui-react";
export default function OctaveShift() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys nbKeys={25} octaveShift={-2} label="2 Octaves Down" size="large" />
<Keys nbKeys={25} octaveShift={0} label="No Shift (Default)" size="large" />
<Keys nbKeys={25} octaveShift={2} label="2 Octaves Up" size="large" />
</div>
);
}Note Highlighting
Highlight active notes using the notesOn prop. Notes can be specified as strings (note names) or MIDI note numbers:
Using Note Names
import { Keys } from "@cutoff/audio-ui-react";
export default function NoteNames() {
return (
<Keys notesOn={["C4", "E4", "G4"]} label="C Major Chord" size="large" />
);
}Using MIDI Note Numbers
import { Keys } from "@cutoff/audio-ui-react";
export default function MIDINotes() {
return (
<Keys notesOn={[60, 64, 67]} label="C Major Chord (MIDI)" size="large" />
);
}Mixing Note Names and MIDI Numbers
import { Keys } from "@cutoff/audio-ui-react";
export default function MixedNotes() {
return (
<Keys notesOn={["C4", 64, "G4"]} label="Mixed Format" size="large" />
);
}Interactive Note Highlighting
Update the highlighted notes based on user interaction:
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
export default function InteractiveNotesExample() {
const [notesOn, setNotesOn] = useState([]);
const handleChange = (e) => {
const noteNum = e.value.note;
if (e.value.active) {
setNotesOn((prev) => [...prev, noteNum]);
} else {
setNotesOn((prev) => prev.filter((n) => n !== noteNum));
}
};
return (
<Keys nbKeys={61} notesOn={notesOn} onChange={handleChange} size="large" />
);
}Key Styling Modes
The Keys component supports three styling modes:
Theme Mode
The default mode uses theme colors (from the color prop or global theme):
import { Keys } from "@cutoff/audio-ui-react";
export default function ThemeMode() {
return (
<Keys
keyStyle="theme"
color="#3b82f6"
label="Theme Mode"
size="large"
/>
);
}Classic Mode
Classic piano style with ivory white keys and ebony black keys:
import { Keys } from "@cutoff/audio-ui-react";
export default function ClassicMode() {
return (
<Keys keyStyle="classic" label="Classic Piano" size="large" />
);
}Classic Inverted Mode
Inverted classic style with ebony white keys and ivory black keys:
import { Keys } from "@cutoff/audio-ui-react";
export default function ClassicInvertedMode() {
return (
<Keys
keyStyle="classic-inverted"
label="Inverted Classic"
size="large"
/>
);
}Note:
In classic and classic-inverted modes, active keys always use the theme color for visual feedback, regardless of the key style.
Theming
Vector components share common theming features; see Theming Features in the Vector introduction for an overview. The Keys component supports theming through the color and roundness props:
Color Customization
In theme mode, customize the color of active keys:
import { Keys } from "@cutoff/audio-ui-react";
export default function KeysColor() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys
keyStyle="theme"
color="#3b82f6"
notesOn={["C4", "E4", "G4"]}
label="Blue Theme"
size="large"
/>
<Keys
keyStyle="theme"
color="#ef4444"
notesOn={["C4", "E4", "G4"]}
label="Red Theme"
size="large"
/>
<Keys
keyStyle="theme"
color="#10b981"
notesOn={["C4", "E4", "G4"]}
label="Green Theme"
size="large"
/>
</div>
);
}Roundness
Control the roundness of the key corners:
import { Keys } from "@cutoff/audio-ui-react";
export default function KeysRoundness() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys roundness={0.0} label="Square Keys" size="large" />
<Keys roundness={0.5} label="Rounded Keys" size="large" />
<Keys roundness={1.0} label="Fully Rounded Keys" size="large" />
</div>
);
}Adaptive Sizing
The Keys component supports both fixed sizes and adaptive sizing. By default, the size of the component is driven by the Size System and their size attribute:
import { Keys } from "@cutoff/audio-ui-react";
export default function KeysSizing() {
return (
<div className="flex gap-4 items-center flex-wrap">
<Keys nbKeys={25} size="small" label="Small" />
<Keys nbKeys={25} size="normal" label="Normal" />
<Keys nbKeys={25} size="large" label="Large" />
<Keys nbKeys={25} size="xlarge" label="XLarge" />
</div>
);
}Adaptive sizing allows the component to adapt to the size of its parent container, adjusting its dimensions intelligently without causing distortion (scaleToFit display mode). The fill display mode makes the component fill the entire container, potentially distorting it.
Interaction
The Keys component becomes interactive when you provide the onChange prop:
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
export default function InteractiveKeysExample() {
const [notesOn, setNotesOn] = useState([]);
const handleChange = (e) => {
const noteNum = e.value.note;
if (e.value.active) {
setNotesOn((prev) => [...prev, noteNum]);
} else {
setNotesOn((prev) => prev.filter((n) => n !== noteNum));
}
};
return (
<Keys
nbKeys={61}
notesOn={notesOn}
onChange={handleChange}
size="large"
/>
);
}The onChange callback receives an event with:
value.note: The MIDI note number (0-127)value.active: Whether the note is being pressed (true) or released (false)normalizedValue: The normalized value (0.0-1.0)midiValue: The MIDI note number
Common Patterns
Full Piano Keyboard
A standard 88-key piano keyboard:
import { Keys } from "@cutoff/audio-ui-react";
export default function FullPiano() {
return (
<Keys nbKeys={88} startKey="A" label="Full Piano" size="large" />
);
}Compact Keyboard
A compact 25-key keyboard for mobile or space-constrained interfaces:
import { Keys } from "@cutoff/audio-ui-react";
export default function CompactKeyboard() {
return (
<Keys nbKeys={25} startKey="C" label="Compact" size="large" />
);
}MIDI Monitor
A keyboard that highlights notes received from MIDI input. In a real app you would connect this to MIDI input; here it shows a static example:
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
export default function MIDIMonitorExample() {
const [notesOn, setNotesOn] = useState([]);
return (
<Keys
nbKeys={61}
notesOn={notesOn}
label="MIDI Monitor"
size="large"
/>
);
}Chord Display
Display a chord by highlighting specific notes:
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
const C_MAJOR = ["C4", "E4", "G4"];
const A_MINOR = ["A3", "C4", "E4"];
const F_MAJOR = ["F3", "A3", "C4"];
export default function ChordDisplayExample() {
const [chord, setChord] = useState(C_MAJOR);
return (
<div className="flex flex-col gap-4">
<div className="flex gap-2">
<button
type="button"
onClick={() => setChord(C_MAJOR)}
className="rounded border px-2 py-1 text-sm"
>
C Major
</button>
<button
type="button"
onClick={() => setChord(A_MINOR)}
className="rounded border px-2 py-1 text-sm"
>
A Minor
</button>
<button
type="button"
onClick={() => setChord(F_MAJOR)}
className="rounded border px-2 py-1 text-sm"
>
F Major
</button>
</div>
<Keys nbKeys={61} notesOn={chord} label="Chord Display" size="large" />
</div>
);
}Layout Considerations
The Keys component maintains proper piano key layout and proportions regardless of the number of keys or starting position. The component:
- Automatically calculates white and black key positions
- Maintains correct spacing between keys
- Handles partial octaves correctly
- Supports any number of keys from 1 to 128
The component uses SVG for rendering, ensuring crisp display at any size and proper scaling.
Common Use Cases
MIDI Keyboard Visualization
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
export default function MIDIKeyboardExample() {
const [activeNotes, setActiveNotes] = useState([]);
const handleNoteChange = (e) => {
if (e.value.active) {
setActiveNotes((prev) => [...prev, e.value.note]);
} else {
setActiveNotes((prev) => prev.filter((n) => n !== e.value.note));
}
};
return (
<Keys
nbKeys={61}
notesOn={activeNotes}
onChange={handleNoteChange}
keyStyle="theme"
size="large"
/>
);
}Piano Roll Display
import { Keys } from "@cutoff/audio-ui-react";
export default function PianoRollExample() {
const playedNotes = [60, 64, 67];
return (
<Keys
nbKeys={88}
startKey="A"
notesOn={playedNotes}
keyStyle="classic"
size="large"
/>
);
}Compact Keyboard Control
import { useState } from "react";
import { Keys } from "@cutoff/audio-ui-react";
export default function CompactKeyboardControlExample() {
const [notesOn, setNotesOn] = useState([]);
const handleNoteChange = (e) => {
if (e.value.active) {
setNotesOn((prev) => [...prev, e.value.note]);
} else {
setNotesOn((prev) => prev.filter((n) => n !== e.value.note));
}
};
return (
<Keys
nbKeys={25}
startKey="C"
octaveShift={-1}
onChange={handleNoteChange}
size="large"
/>
);
}Next Steps
- Explore other vector components like Knob and Slider
- Learn about raster components for bitmap-based controls
- Visit the playground for interactive examples