Manta Developers
GitHub
Menu

Layout and data

Surfaces, dividers, tabs, tables, trees, and navigation that structure a page and its data.

Card

Card groups related content on one surface. Compose it from CardHeader, CardTitle, CardDescription, CardAction, CardContent, and CardFooter. Set padding to sm, md, or lg for a padded card, and surface to muted or dashed for a quieter one.

Today
3 of 5 todos done
Next up: review the launch checklist.
Updated 5 minutes ago
import { Plus } from "lucide-react";
import { Button } from "@manta-eu/react/button";
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@manta-eu/react/card";

<Card className="max-w-sm">
  <CardHeader>
    <CardTitle>Today</CardTitle>
    <CardDescription>3 of 5 todos done</CardDescription>
    <CardAction>
      <Button variant="ghost" icon={Plus} iconOnly aria-label="Add a todo" />
    </CardAction>
  </CardHeader>
  <CardContent className="typography-small">Next up: review the launch checklist.</CardContent>
  <CardFooter className="typography-small text-muted-foreground">Updated 5 minutes ago</CardFooter>
</Card>;

Props · Storybook

Separator

Separator draws a thin line between pieces of content. It is horizontal by default. Set orientation="vertical" to divide items in a row, and give the row a height.

Write the release notes

Due Friday
High priority
Ana
import { Separator } from "@manta-eu/react/separator";

<div className="flex max-w-sm flex-col gap-4">
  <p className="typography-small">Write the release notes</p>
  <Separator />
  <div className="flex h-5 items-center gap-4 typography-small text-muted-foreground">
    <span>Due Friday</span>
    <Separator orientation="vertical" />
    <span>High priority</span>
    <Separator orientation="vertical" />
    <span>Ana</span>
  </div>
</div>;

Props · Storybook

Tabs

Tabs switches between panels that share one place on the page. Give each TabsTrigger and its TabsContent the same value, and pick the starting tab with defaultValue. Set size="small" for a compact list, and add a TabCount to show a count in a trigger.

Todos that still need work.
import { TabCount, Tabs, TabsContent, TabsList, TabsTrigger } from "@manta-eu/react/tabs";

<Tabs defaultValue="active">
  <TabsList>
    <TabsTrigger value="all">
      All <TabCount count={5} />
    </TabsTrigger>
    <TabsTrigger value="active">
      Active <TabCount count={2} />
    </TabsTrigger>
    <TabsTrigger value="completed">
      Completed <TabCount count={3} />
    </TabsTrigger>
  </TabsList>
  <TabsContent value="all">Every todo in the project.</TabsContent>
  <TabsContent value="active">Todos that still need work.</TabsContent>
  <TabsContent value="completed">Todos you have checked off.</TabsContent>
</Tabs>;

Props · Storybook

Table

SortableTable renders rows from plain columns and rows, and sorts a column when the user clicks its header. Mark a column numeric to right-align it, and pass cell to render a custom value. For full control, build a TanStack table and render it with DataTable, or compose Table, TableHeader, TableRow, TableHead, and TableCell by hand.

Todo
Assignee
Status
Hours
Write the release notesAnaActive
2
Review the launch checklistMarkoActive
1
Update the pricing pageIvanaCompleted
3
import { SortableTable } from "@manta-eu/react/table";

type Todo = { title: string; assignee: string; status: string; estimate: number };

const todos: Todo[] = [
  { title: "Write the release notes", assignee: "Ana", status: "Active", estimate: 2 },
  { title: "Review the launch checklist", assignee: "Marko", status: "Active", estimate: 1 },
  { title: "Update the pricing page", assignee: "Ivana", status: "Completed", estimate: 3 },
];

<SortableTable
  rows={todos}
  columns={[
    { key: "title", header: "Todo" },
    { key: "assignee", header: "Assignee" },
    { key: "status", header: "Status" },
    { key: "estimate", header: "Hours", numeric: true },
  ]}
/>;

Props · Storybook

Pagination

PaginationBlock shows the current page and buttons to move between pages. It is controlled: pass a zero-based currentPageIndex, totalPages, and onPageChange. Add showPageSize with pageSize and totalCount to show the range of rows on the page. Compose Pagination, PaginationContent, and the button parts for a custom layout.

1-25 of 87
Page 1 of 4
import { useState } from "react";
import { PaginationBlock } from "@manta-eu/react/pagination";

const [page, setPage] = useState(0);

<PaginationBlock
  currentPageIndex={page}
  totalPages={4}
  onPageChange={setPage}
  showPageSize
  pageSize={25}
  totalCount={87}
/>;

Props · Storybook

Tree

Tree shows nested items that the user expands, collapses, and selects with the mouse or the arrow keys. Pass nodes with an id, a label, and optional icon, children, defaultExpanded, and disabled. Use defaultSelectedId on its own, or selectedId with onSelect to control the selection.

  • Website
    • Update the pricing page
    • Publish the launch post
import { FileText, Folder } from "lucide-react";
import { Tree, type TreeNodeData } from "@manta-eu/react/tree";

const projects: TreeNodeData[] = [
  {
    id: "website",
    label: "Website",
    icon: <Folder />,
    defaultExpanded: true,
    children: [
      { id: "pricing", label: "Update the pricing page", icon: <FileText /> },
      { id: "blog", label: "Publish the launch post", icon: <FileText /> },
    ],
  },
  {
    id: "mobile",
    label: "Mobile app",
    icon: <Folder />,
    children: [{ id: "onboarding", label: "Redesign onboarding", icon: <FileText /> }],
  },
];

<Tree nodes={projects} defaultSelectedId="pricing" aria-label="Projects" className="max-w-xs" />;

Props · Storybook

Collapsible

Collapsible shows and hides a section when the user clicks its trigger. Put the toggle in CollapsibleTrigger and the hidden part in CollapsibleContent. Control it with open and onOpenChange, or start it open with defaultOpen.

import { useState } from "react";
import { ChevronRight } from "lucide-react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@manta-eu/react/collapsible";

const [open, setOpen] = useState(false);

<Collapsible open={open} onOpenChange={setOpen} className="max-w-sm rounded-lg border border-default-border p-4">
  <CollapsibleTrigger className="group flex w-full items-center gap-2">
    <span className="typography-small-header">Completed todos</span>
    <ChevronRight className="size-4 transition-transform group-data-[state=open]:rotate-90" />
  </CollapsibleTrigger>
  <CollapsibleContent>
    <ul className="mt-3 list-disc space-y-1 pl-5 typography-small">
      <li>Update the pricing page</li>
      <li>Book the venue</li>
      <li>Send the invites</li>
    </ul>
  </CollapsibleContent>
</Collapsible>;

Props · Storybook

Breadcrumbs shows where the current page sits in the hierarchy. Pass items with a label and an href or onClick. The last item marks the current page. Items with an href render a plain <a>, so no router is needed. Pass renderLink to use your router’s link instead.

import { Home, ListTodo } from "lucide-react";
import { Breadcrumbs } from "@manta-eu/react/breadcrumbs";

<Breadcrumbs
  items={[
    { label: "Home", href: "#", icon: <Home /> },
    { label: "Website", href: "#", icon: <ListTodo /> },
    { label: "Update the pricing page" },
  ]}
/>;

Props · Storybook