React Registry

primitiveTimecodeInput@techsquidtv/canvas-timeline-react/timecode-input

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.

Basic timecode input

An always-visible controlled text box for typing and validating timecode text.

Installation

pnpm
pnpm add @techsquidtv/canvas-timeline-react
  1. 1

    Install the package.

    The command above adds the React bindings and their package dependencies.

  2. 2

    Import from the React entrypoint.

    @techsquidtv/canvas-timeline-react/timecode-input
  3. 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

ClipStartTimecode.tsx
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
Example source
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 TimecodeInput for 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 invalid when 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:FF requires a frameRate in both format and parse options.
  • Convert parsed seconds with fromSeconds(parsed, sequenceRate) so RationalTime performs final tick rounding at your app boundary.
  • Choose TimecodeField instead 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

NameReferenceDescription
TimecodeInputAPI referenceRenders the always-visible text input surface for timeline-position text.
TimecodeInputPropsAPI referenceBase UI Input props plus invalid styling for form wrappers that own validation feedback.
formatTimecodeInputAPI referenceFormats seconds as decimal clock text, total seconds/minutes, or frame-rate timecode.
parseTimecodeInputAPI referenceConverts decimal or frame timecode text into precise seconds, or null when invalid.
TimecodeFrameRateAPI referenceNumber or rational frame rate accepted by frame-based timecode helpers.
TimecodeInputFormatAPI referenceAllowed output formats for formatting timecode input text.
TimecodeInputFormatOptionsAPI referenceOptions object accepted by formatTimecodeInput.
TimecodeInputParseRoundingAPI referenceOptional rounding policy for parsed timecode input text.
TimecodeInputParseOptionsAPI referenceOptions object accepted by parseTimecodeInput.

Props and Inputs

NameTypeDescription
invalidbooleanMarks the input invalid when parseTimecodeInput returns null.
valuestringCurrent input text, such as "90", "1:30.25", or "00:01:30:12".
onValueChange(value, details) => voidReceives text as the user types so you can parse it or store it in form state.
classNamestringAdds design-system classes while preserving the timecode-input slot class.