Basic timecode input
An always-visible controlled text box for typing and validating timecode text.
Installation
pnpm add @techsquidtv/canvas-timeline-reactnpm install @techsquidtv/canvas-timeline-reactyarn add @techsquidtv/canvas-timeline-reactbun add @techsquidtv/canvas-timeline-react- 1
Install the package.
The command above adds the React bindings and their package dependencies.
- 2
Import from the React entrypoint.
@techsquidtv/canvas-timeline-react/timecode-input - 3
Add the timeline styles.
Use the full stylesheet with shadcn-compatible tokens, or base.css when your app owns the visual theme.
Default visuals import '@techsquidtv/canvas-timeline-react/styles.css';Base geometry import '@techsquidtv/canvas-timeline-react/base.css';
Usage
import { useState } from 'react';import { TimecodeInput, parseTimecodeInput,} from '@techsquidtv/canvas-timeline-react/timecode-input';
export function ClipStartTimecode() { const [text, setText] = useState('90.5'); const invalid = parseTimecodeInput(text) === null;
return ( <TimecodeInput aria-label="Clip start" value={text} invalid={invalid} onValueChange={setText} /> );}Examples
Formatting
Keep the text box visible, parse its draft value, then rewrite that text when the selected display format changes.
3723.0400s
import { type FormEvent, useState } from 'react';import { fromSeconds, type RationalTime } from '@techsquidtv/canvas-timeline-utils';import { TimecodeInput, type TimecodeInputFormatOptions, type TimecodeInputParseOptions, formatTimecodeInput, parseTimecodeInput,} from '@techsquidtv/canvas-timeline-react/timecode-input';
const formatOptions = [ { value: 'seconds', label: 'Seconds', formatOptions: { format: 'seconds' } }, { value: 'minutes', label: 'Minutes', formatOptions: { format: 'minutes' } }, { value: 'frames-24', label: '24 fps', formatOptions: { format: 'frames', frameRate: 24 }, parseOptions: { frameRate: 24 }, },] satisfies Array<{ value: string; label: string; formatOptions: TimecodeInputFormatOptions; parseOptions?: TimecodeInputParseOptions;}>;const initialSeconds = 90.5;const sequenceRate = 24000;
export function ClipStartTimecode({ onApply,}: { onApply: (time: RationalTime) => void;}) { const [formatValue, setFormatValue] = useState('seconds'); const [text, setText] = useState(() => formatTimecodeInput(initialSeconds, { format: 'seconds' }) ); const selectedFormat = formatOptions.find((option) => option.value === formatValue) ?? formatOptions[0]; const parsedSeconds = parseTimecodeInput(text, selectedFormat.parseOptions);
function handleFormatChange(nextFormatValue: string) { const nextFormat = formatOptions.find((option) => option.value === nextFormatValue); const nextSeconds = parseTimecodeInput(text, selectedFormat.parseOptions);
if (!nextFormat) { return; }
setFormatValue(nextFormat.value);
if (nextSeconds !== null) { setText(formatTimecodeInput(nextSeconds, nextFormat.formatOptions)); } }
function handleSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault();
if (parsedSeconds === null) { return; }
onApply(fromSeconds(parsedSeconds, sequenceRate)); setText(formatTimecodeInput(parsedSeconds, selectedFormat.formatOptions)); }
return ( <form onSubmit={handleSubmit}> <TimecodeInput aria-label="Clip start" value={text} invalid={parsedSeconds === null} onValueChange={setText} /> <select aria-label="Timecode format" value={formatValue} onChange={(event) => handleFormatChange(event.currentTarget.value)} > {formatOptions.map((option) => ( <option key={option.value} value={option.value}> {parsedSeconds === null ? option.label : `${option.label} (${formatTimecodeInput(parsedSeconds, { ...option.formatOptions, })})`} </option> ))} </select> <button disabled={parsedSeconds === null} type="submit"> Apply </button> </form> );}Notes
- Choose
TimecodeInputfor form-like controls where the editable text box is always visible. - Supports typing unit suffixes like "60s" (seconds), "500ms" (milliseconds), "2m" (minutes), or "24f" (frames, requiring frameRate).
- You own the surrounding label, submit/apply action, error message, and conversion from parsed seconds into RationalTime.
- Pass
invalidwhen parsing fails so your own field wrapper can show an error message or disable an Apply action. - Parsing preserves entered decimal precision by default; pass rounding: "centisecond" only for legacy or display-like workflows.
- Frame text such as
HH:MM:SS:FFrequires aframeRatein both format and parse options. - Convert parsed seconds with
fromSeconds(parsed, sequenceRate)soRationalTimeperforms final tick rounding at your app boundary. - Choose
TimecodeFieldinstead when the value should read like compact editor chrome until the user activates it.
API Reference
Timecode Input
An always-editable text input for timecode forms: users type seconds, m:ss, h:mm:ss, or frame text while your UI owns validation, labels, and apply behavior.
Exports
| Name | Reference | Description |
|---|---|---|
TimecodeInput | API reference | Renders the always-visible text input surface for timeline-position text. |
TimecodeInputProps | API reference | Base UI Input props plus invalid styling for form wrappers that own validation feedback. |
formatTimecodeInput | API reference | Formats seconds as decimal clock text, total seconds/minutes, or frame-rate timecode. |
parseTimecodeInput | API reference | Converts decimal or frame timecode text into precise seconds, or null when invalid. |
TimecodeFrameRate | API reference | Number or rational frame rate accepted by frame-based timecode helpers. |
TimecodeInputFormat | API reference | Allowed output formats for formatting timecode input text. |
TimecodeInputFormatOptions | API reference | Options object accepted by formatTimecodeInput. |
TimecodeInputParseRounding | API reference | Optional rounding policy for parsed timecode input text. |
TimecodeInputParseOptions | API reference | Options object accepted by parseTimecodeInput. |
Props and Inputs
| Name | Type | Description |
|---|---|---|
invalid | boolean | Marks the input invalid when parseTimecodeInput returns null. |
value | string | Current input text, such as "90", "1:30.25", or "00:01:30:12". |
onValueChange | (value, details) => void | Receives text as the user types so you can parse it or store it in form state. |
className | string | Adds design-system classes while preserving the timecode-input slot class. |