Skip to content
Blog

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

CategoryCountPurpose
Navigation16App shells, sidebars, top bars, breadcrumbs
Layout12Stacks, grids, centering, form layout
Buttons5Button variants, icon buttons, toggle groups
Inputs18Text inputs, selects, switches, file inputs
Selectors10Dropdowns, typeaheads, radio lists, tokenizers
Data Display22Tables, cards, lists, avatars, badges, code blocks
Overlays13Dialogs, popovers, tooltips, command palette
Feedback5Banners, toasts, progress bars, skeletons, spinners
Typography8Headings, text, blockquotes, keyboard, links
Tabs3Tab lists, tabs, tab menus
Chat14Chat composers, message bubbles, tool calls
Utilities8Collapsible, divider, theme switchers, providers
Tooling4Pagination, search, toolbar, overflow list

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