React
Typed components that render mica's markup, from server components to hydrated client ones.
The React components live in the same package as the stylesheet. Each one renders the markup the rest of these docs show, nothing more: the browser and mica's modules supply the behavior, exactly as they do for plain HTML. They add typed props, and they load the few modules a component needs.
Install
React 19 or later. React is an optional peer dependency: projects that only use the stylesheet don't need it.
npm install @akonwi/mica
Import the stylesheet once, where your app starts: the root layout in Next.js, the entry file in Vite. The components never import it for you.
import "@akonwi/mica/mica.css";
Mica's CSS lives in cascade layers, so your own unlayered CSS always wins without specificity tricks. See tokens & theming.
A first component
import { Button, Card, CardBody, CardFooter, VStack } from "@akonwi/mica/react";
export function Welcome() {
return (
<Card>
<CardBody>
<VStack gap="sm">
<h2>Welcome back</h2>
<p>Pick up where you left off.</p>
</VStack>
</CardBody>
<CardFooter divider>
<Button type="button" variant="primary">Continue</Button>
</CardFooter>
</Card>
);
}
That renders exactly this, the same markup the card, vstack, and button pages document:
<m-card>
<m-card-body>
<m-vstack gap="sm">
<h2>Welcome back</h2>
<p>Pick up where you left off.</p>
</m-vstack>
</m-card-body>
<m-card-footer divider>
<button type="button" data-variant="primary">Continue</button>
</m-card-footer>
</m-card>
Props mirror the attributes: gap is gap,
variant becomes data-variant, and kebab-case
attributes become camelCase (side-width is
sideWidth). Native defaults stay native: a
Button is a submit button unless you say otherwise, as in
HTML.
Reading these docs for React
Every component page has an HTML | React switch at the top. The React edition shows the JSX for each code block and runs its examples as React. Prose and API tables are shared; read an attribute in a table as a prop by the rules above.
Server components and modules
The components work in server components. Layout, cards, lists, buttons,
fields, tables, and the other components without state of their own have
no "use client" and run on the server. The ones that hold state
or listen for events are client components: dialogs, popovers,
accordions, tabs, menus, the combobox, the header, the sidebar, checkbox,
segmented, and useToast. You can still render them from a server
file; every export is a flat named export, so there is nothing to dot into.
// app/page.tsx: a server component, no "use client".
import { Card, CardBody, Tab, Tabs, TabsList, TabsPanel, VStack } from "@akonwi/mica/react";
export default function Page() {
return (
<VStack gap="lg">
<Card>
<CardBody><p>Rendered on the server; no JavaScript for this part.</p></CardBody>
</Card>
<Tabs defaultValue="activity">
<TabsList aria-label="Project">
<Tab value="activity">Activity</Tab>
<Tab value="settings">Settings</Tab>
</TabsList>
<TabsPanel value="activity">…</TabsPanel>
<TabsPanel value="settings">…</TabsPanel>
</Tabs>
</VStack>
);
}
A component that needs an enhancement module loads it on the client after
hydration: Tabs loads tabs.js, Menu
loads menu.js, and so on. Each module is its own chunk, fetched
only when a component that needs it mounts, and never runs on the server.
The server-rendered markup is complete without it, so the page reads
correctly before the module arrives, and stays usable if it never does.
Hydration is handled for you. While React hydrates, the components mark
their hosts with data-m-pending, and a module that has already
loaded leaves those hosts alone until React is done. Plain HTML never needs
this.
Plain tags
You don't have to use the components. React 19 renders custom elements
directly, so <m-vstack gap="lg"> works in JSX as it does
in HTML. For types, reference mica's JSX typings once. To enhance plain
markup from a component, render MicaModule with the module's
name.
/// <reference types="@akonwi/mica/types/react" />
import { MicaModule } from "@akonwi/mica/react";
export function Settings() {
return (
<>
<m-tabs>
<nav aria-label="Settings">
<button type="button">Profile</button>
<button type="button">Billing</button>
</nav>
<section>…profile…</section>
<section>…billing…</section>
</m-tabs>
<MicaModule name="tabs" />
</>
);
}
Plain tags don't get the hydration gate. Where a module changes the markup it enhances (tabs, the combobox, the command palette, menus, the header, the sidebar), prefer the component in server-rendered pages: if the module is already loaded while that markup is still hydrating, it would edit markup React hasn't adopted yet.
Native elements have no wrapper: inputs, textareas, radios, selects, fieldsets, progress, and meter are styled as they are, so you write them as plain JSX. A component exists only where mica adds something JSX can't say directly: an attribute vocabulary, a set of parts, wiring between elements, or state a native attribute can't carry.