Manta Developers
GitHub
Menu

Forms

Text fields, choices, pickers, and the other controls that collect input from the user.

Input

Input is a single-line text field. Pass label to name it, and helperText or error to show a message below it. Pick a size from xs to lg.

import { useState } from "react";
import { Input } from "@manta-eu/react/input";

const [title, setTitle] = useState("Write the release notes");

<Input
  label="Title"
  placeholder="What needs doing?"
  value={title}
  onChange={(event) => setTitle(event.target.value)}
  error={title.trim() === "" ? "Give the todo a title" : undefined}
/>;

Props · Storybook

Textarea

Textarea is a multi-line text field with the same label and error props as Input. Set maxLength with showCharacterCount to show a counter, and minRows or maxRows to bound its height.

34/200
import { useState } from "react";
import { Textarea } from "@manta-eu/react/input";

const [notes, setNotes] = useState("Cover the new design system pages.");

<Textarea
  label="Notes"
  placeholder="Add details"
  value={notes}
  onChange={(event) => setNotes(event.target.value)}
  maxLength={200}
  showCharacterCount
/>;

Props · Storybook

InputNumeric

InputNumeric is a number field with stepper buttons. It is controlled: pass value and onValueChange. Set min, max, and step to bound the value. The arrow keys step it too.

import { useState } from "react";
import { InputNumeric } from "@manta-eu/react/input";

const [estimate, setEstimate] = useState(2);

<InputNumeric
  value={estimate}
  onValueChange={setEstimate}
  min={0}
  max={40}
  step={0.5}
  aria-label="Estimate in hours"
/>;

Props · Storybook

InputTags

InputTags turns typed text into removable chips. The user presses Enter or a comma to add a tag. It is controlled: pass a value array and onChange.

docsrelease
import { useState } from "react";
import { InputTags } from "@manta-eu/react/input";

const [tags, setTags] = useState(["docs", "release"]);

<InputTags value={tags} onChange={setTags} placeholder="Add a tag…" aria-label="Tags" />;

Props · Storybook

InputOTP

InputOTP collects a one-time code, one character per slot. Set maxLength and render one InputOTPSlot per character inside an InputOTPGroup. Add InputOTPSeparator to split the slots.

import { useState } from "react";
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@manta-eu/react/input-otp";

const [code, setCode] = useState("");

<InputOTP maxLength={6} value={code} onChange={setCode} aria-label="Verification code">
  <InputOTPGroup>
    <InputOTPSlot index={0} />
    <InputOTPSlot index={1} />
    <InputOTPSlot index={2} />
    <InputOTPSlot index={3} />
    <InputOTPSlot index={4} />
    <InputOTPSlot index={5} />
  </InputOTPGroup>
</InputOTP>;

Props · Storybook

Checkbox

Checkbox toggles one option on or off. Pass label to name it, and checked with onCheckedChange to control it. Set checked to "indeterminate" for a partial selection.

import { useState } from "react";
import { Checkbox } from "@manta-eu/react/checkbox";

const [done, setDone] = useState(true);

<Checkbox label="Write the release notes" checked={done} onCheckedChange={(checked) => setDone(checked === true)} />;

Props · Storybook

Radio

RadioGroup lets the user pick one option from a short list. Give each RadioGroupItem a value and a label. Set size="large" for a bordered card with an icon and a description.

import { useState } from "react";
import { RadioGroup, RadioGroupItem } from "@manta-eu/react/radio";

const [priority, setPriority] = useState("medium");

<RadioGroup value={priority} onValueChange={setPriority} aria-label="Priority">
  <RadioGroupItem value="low" label="Low" />
  <RadioGroupItem value="medium" label="Medium" />
  <RadioGroupItem value="high" label="High" />
</RadioGroup>;

Props · Storybook

Switch

Switch turns a setting on or off right away. Pass checked and onCheckedChange, and add label and description to explain the setting.

Send an email the day before a todo is due.

import { useState } from "react";
import { Switch } from "@manta-eu/react/switch";

const [reminders, setReminders] = useState(true);

<Switch
  label="Due date reminders"
  description="Send an email the day before a todo is due."
  checked={reminders}
  onCheckedChange={setReminders}
/>;

Props · Storybook

Select

Select picks one value from a dropdown list. SelectTrigger needs an id and a label; add hiddenLabel to hide the label visually. Put one SelectItem per option inside SelectContent.

import { useState } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@manta-eu/react/select";

const [status, setStatus] = useState("in-progress");

<Select value={status} onValueChange={setStatus}>
  <SelectTrigger id="todo-status" label="Status" className="w-[180px]">
    <SelectValue placeholder="Pick a status" />
  </SelectTrigger>
  <SelectContent>
    <SelectItem value="todo">To do</SelectItem>
    <SelectItem value="in-progress">In progress</SelectItem>
    <SelectItem value="done">Done</SelectItem>
  </SelectContent>
</Select>;

Props · Storybook

Combobox

Combobox is a popover with a searchable list. Wrap any trigger in ComboboxTrigger, then fill ComboboxContent with a ComboboxInput and a ComboboxList of ComboboxItems. An item closes the popover on select unless you set closeOnSelect={false}.

import { useState } from "react";
import { Button } from "@manta-eu/react/button";
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
  ComboboxTrigger,
} from "@manta-eu/react/combobox";

const assignees = [
  { value: "ana", label: "Ana Horvat" },
  { value: "marko", label: "Marko Petrović" },
  { value: "sara", label: "Sara Novak" },
];

const [assignee, setAssignee] = useState<string>();
const selected = assignees.find((person) => person.value === assignee);

<Combobox>
  <ComboboxTrigger asChild>
    <Button variant="tertiary" className="min-w-[180px]">
      {selected?.label ?? "Assign to…"}
    </Button>
  </ComboboxTrigger>
  <ComboboxContent>
    <ComboboxInput placeholder="Search people…" />
    <ComboboxList>
      <ComboboxEmpty>No one found.</ComboboxEmpty>
      {assignees.map((person) => (
        <ComboboxItem
          key={person.value}
          value={person.label}
          selected={assignee === person.value}
          onSelect={() => setAssignee(person.value)}
        >
          {person.label}
        </ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>;

Props · Storybook

MultiSelect

MultiSelect picks several values from a searchable list and shows them as chips. Pass options, a value array, and onChange. Add clearable for a button that removes every value.

Docs
import { useState } from "react";
import { MultiSelect } from "@manta-eu/react/multi-select";

const labels = [
  { value: "bug", label: "Bug" },
  { value: "docs", label: "Docs" },
  { value: "design", label: "Design" },
  { value: "backend", label: "Backend" },
];

const [selected, setSelected] = useState(["docs"]);

<MultiSelect
  options={labels}
  value={selected}
  onChange={setSelected}
  placeholder="Add labels"
  clearable
  aria-label="Labels"
  className="w-80"
/>;

Props · Storybook

Slider

Slider picks a number by dragging a thumb along a track. Its value is an array: pass two numbers for a range. Use thumbProps to give each thumb an aria-label.

40% done

import { useState } from "react";
import { Slider } from "@manta-eu/react/slider";

const [progress, setProgress] = useState([40]);

<div className="flex w-80 flex-col gap-2">
  <Slider value={progress} onValueChange={setProgress} step={10} thumbProps={[{ "aria-label": "Progress" }]} />
  <p className="typography-caption text-muted-foreground">{progress[0]}% done</p>
</div>;

Props · Storybook

Calendar

Calendar shows a month grid for picking dates. It wraps react-day-picker: set mode to single, multiple, or range, and pass selected with onSelect.

November 2026
import { useState } from "react";
import { Calendar } from "@manta-eu/react/calendar";

const [dueDate, setDueDate] = useState<Date | undefined>(new Date(2026, 10, 14));

<Calendar mode="single" selected={dueDate} onSelect={setDueDate} defaultMonth={dueDate} />;

Props · Storybook