Astryx Component Architecture: 150+ Components Across 13 Categories
Explore all 150+ Astryx components organized into 13 categories — navigation, layout, buttons, inputs, selectors, data display, overlays, feedback, and more with code examples.
Published on • July 31, 2026
AI Assistant

Astryx ships over 150 components organized into 13 categories. Every component handles its own layout, styling, and accessibility — you never reach for raw <div> or <span> elements. This tutorial walks through all categories with code examples and practical patterns.
Component Categories at a Glance
| Category | Count | Purpose |
|---|---|---|
| Navigation | 16 | App shells, sidebars, top bars, breadcrumbs |
| Layout | 12 | Stacks, grids, centering, form layout |
| Buttons | 5 | Button variants, icon buttons, toggle groups |
| Inputs | 18 | Text inputs, selects, switches, file inputs |
| Selectors | 10 | Dropdowns, typeaheads, radio lists, tokenizers |
| Data Display | 22 | Tables, cards, lists, avatars, badges, code blocks |
| Overlays | 13 | Dialogs, popovers, tooltips, command palette |
| Feedback | 5 | Banners, toasts, progress bars, skeletons, spinners |
| Typography | 8 | Headings, text, blockquotes, keyboard, links |
| Tabs | 3 | Tab lists, tabs, tab menus |
| Chat | 14 | Chat composers, message bubbles, tool calls |
| Utilities | 8 | Collapsible, divider, theme switchers, providers |
| Tooling | 4 | Pagination, search, toolbar, overflow list |
Navigation (16)
The shell of your application. Start here before writing any content.
<AppShell
sideNav={
<SideNav collapsible
header={<SideNavHeading icon={<NavIcon icon={<DashboardIcon />} />}
heading="My App" headingHref="/" />}
>
<SideNavSection title="Main">
<SideNavItem icon={DashboardIcon} label="Dashboard" isSelected href="/" />
<SideNavItem icon={SettingsIcon} label="Settings" href="/settings" />
</SideNavSection>
</SideNav>
}
topNav={
<TopNav label="Main navigation"
heading={<TopNavHeading heading="Dashboard" />}
endContent={<IconButton icon={UserIcon} label="Profile" />}
/>
}
>
{/* Main content */}
</AppShell>
Layout (12)
The building blocks for arranging content:
import { HStack, VStack } from '@astryxdesign/core/Stack';
import { Grid, GridSpan } from '@astryxdesign/core/Grid';
import { Center } from '@astryxdesign/core/Center';
import { FormLayout } from '@astryxdesign/core/FormLayout';
<VStack gap={3}>
<HStack gap={2} wrap>
<Button label="Save" variant="primary" />
<Button label="Cancel" variant="ghost" />
</HStack>
<Grid columns={3} gap={3}>
<Card>Item 1</Card>
<GridSpan columns={2}><Card>Spans 2 columns</Card></GridSpan>
</Grid>
</VStack>
Buttons (5)
<Button label="Primary" variant="primary" />
<Button label="Secondary" variant="secondary" />
<Button label="Danger" variant="danger" />
<Button label="Ghost" variant="ghost" />
<IconButton icon={EditIcon} label="Edit" />
<ToggleButton label="Bold" isSelected onChange={handleToggle} />
<ButtonGroup>
<Button label="Day" />
<Button label="Week" />
<Button label="Month" />
</ButtonGroup>
Inputs (18)
All inputs use semantic color tokens automatically and support error state via the status prop:
<TextInput label="Email" type="email"
value={email} onChange={setEmail}
placeholder="you@example.com" isRequired
status={errors.email ? { type: 'error', message: errors.email } : undefined}
/>
<TextArea label="Description" value={value} onChange={setValue} />
<NumberInput label="Quantity" min={1} max={10} isIntegerOnly />
<Switch label="Dark mode" value={isDark} onChange={setIsDark} />
<DateInput label="Start date" value={startDate} onChange={setStartDate} />
<Slider label="Volume" min={0} max={100} value={volume} onChange={setVolume} />
Selectors (10)
<Selector label="Region" value={region} onChange={setRegion}
options={[
{ value: 'na', label: 'North America' },
{ value: 'eu', label: 'Europe' },
]}
hasSearch
/>
<Typeahead label="Search users" options={userOptions} onSelect={handleSelect} />
<Tokenizer label="Tags" value={tags} onChange={setTags} />
Data Display (22)
Table (Data-Driven API)
<Table
data={rows}
idKey="id"
columns={[
{ key: 'name', header: 'Name', width: proportional(2) },
{ key: 'role', header: 'Role', width: proportional(1) },
]}
density="compact"
dividers="rows"
hasHover
/>
Card
<Card padding={3} variant="muted">
<VStack gap={2}>
<Heading level={3}>Metric Title</Heading>
<Heading level={1}>$42,500</Heading>
</VStack>
</Card>
<ClickableCard onClick={handleClick}>
<Text>Gallery item</Text>
</ClickableCard>
Overlays (13)
<Dialog open={isOpen} onClose={handleClose} title="Confirm" purpose="form" width={480}>
<Text>Are you sure you want to delete this item?</Text>
<HStack gap={2}>
<Button label="Delete" variant="danger" onClick={handleDelete} />
<Button label="Cancel" variant="ghost" onClick={handleClose} />
</HStack>
</Dialog>
<Popover content={<SettingsPanel />}>
<Button label="Settings" variant="ghost" />
</Popover>
<Tooltip content="This field is required">
<TextInput label="Name" />
</Tooltip>
<CommandPalette open={isOpen} onClose={handleClose}
commands={[
{ id: 'new-file', label: 'New file', icon: FileIcon, shortcut: '⌘N' },
]}
/>
Feedback (5)
<Banner status="error" title="Connection lost" description="Please check your network." />
<Toast status="success" title="Saved successfully" onDismiss={handleDismiss} />
<ProgressBar value={75} max={100} />
<Skeleton width="200px" height="20px" />
<Spinner size="md" label="Loading..." />
Chat (14)
<ChatLayout density="spacious"
composer={
<ChatComposer onSubmit={handleSend} placeholder="Type a message..."
input={<ChatComposerInput />}
/>
}
>
<ChatMessageList>
<ChatMessage sender="assistant" avatar={<Avatar name="AI" size="md" />}>
<ChatMessageBubble variant="ghost">How can I help you?</ChatMessageBubble>
<ChatMessageMetadata timestamp={<Timestamp value="10:15" format="time" />} />
</ChatMessage>
<ChatMessage sender="user">
<ChatMessageBubble>Show my dashboard</ChatMessageBubble>
</ChatMessage>
<ChatMessage sender="assistant">
<ChatMessageBubble variant="ghost">
<ChatTokenizedText tokens={TOKENS}>
@agent Here are the results
</ChatTokenizedText>
</ChatMessageBubble>
<ChatToolCalls calls={[
{ name: 'query', target: 'dashboard', status: 'complete', duration: '45ms' },
]} />
</ChatMessage>
</ChatMessageList>
</ChatLayout>
Key Patterns
Compound Components
Every sub-component accepts its own xstyle, className, and rest props:
<Dialog open={isOpen} onClose={close} xstyle={overrides.dialog}>
<Layout
header={<LayoutHeader><Heading level={2}>Title</Heading></LayoutHeader>}
content={<LayoutContent xstyle={overrides.content}>Body</LayoutContent>}
footer={<LayoutFooter>
<Button label="Cancel" />
<Button label="Save" />
</LayoutFooter>}
/>
</Dialog>
Styles via Props
Components reflect all significant props as data-* attributes on the root element:
.my-app .astryx-button[data-variant="primary"] { }
.my-app .astryx-heading[data-level="2"] { }
Discovery Commands
npx astryx component --list # all components
npx astryx component Button # props + examples
npx astryx component Button --dense # compact (AI-friendly)
npx astryx component Button --json # programmatic
npx astryx search "table" # find related components