Intentwise Primitives
Controls
Interactive filter, selector, and toggle components for dashboard toolbars and settings panels.
FilterSelect
Multi-Select Mode
Select multiple options from a dropdown list. Supports search, select all, and clear.
Selected: None
Show code
<FilterSelect
data={{ label: 'Campaign Type', options: [...], value: [], mode: 'multi' }}
onChange={(value) => setSelected(value)}
searchable
/>Single-Select Mode
Select a single option from a dropdown. Useful for status or category filters.
Selected: active
Show code
<FilterSelect
data={{ label: 'Status', options: [...], value: 'active', mode: 'single' }}
onChange={(value) => setStatus(value)}
/>Loading & Disabled States
FilterSelect supports loading spinners and disabled state.
Show code
<FilterSelect data={...} onChange={...} loading />
<FilterSelect data={...} onChange={...} disabled />FilterBar
FilterBar Layout
Responsive grid container that arranges filter controls in a consistent row. Adapts from 1 column on mobile to 4 on large screens.
Show code
<FilterBar>
<FilterSelect data={campaignType} onChange={...} />
<FilterSelect data={status} onChange={...} />
<FilterSelect data={brand} onChange={...} />
</FilterBar>SegmentedControl
Default Size
Radio-group style toggle for switching between options. Commonly used for time range, view mode, or metric selectors.
Time Range
View Mode
Show code
<SegmentedControl
data={{ options: [{ value: '7d', label: '7D' }, ...], value: '30d' }}
onChange={(value) => setTimeRange(value)}
/>Size Variants
Available in sm and md (default) sizes.
sm
md
Show code
<SegmentedControl data={...} onChange={...} size="sm" />OrgAccountSelector
Organization & Account Selector
Cascading dropdown pair for selecting organization and account. The account dropdown is disabled until an organization is selected. Changing org resets the account.
Org: org-1 | Account: acc-1
Show code
<OrgAccountSelector
data={{ organizations: [...], accounts: [...], selectedOrgId, selectedAccountId }}
onOrgChange={(id) => setOrgId(id)}
onAccountChange={(id) => setAccountId(id)}
/>Loading States
Loading spinners display while data is being fetched from the server.
Show code
<OrgAccountSelector data={...} orgsLoading accountsLoading />Disabled State
Both dropdowns disabled when the component is in disabled state.
Show code
<OrgAccountSelector data={...} disabled />FilterChipBar
FilterChipBar
Chips for applied filters with reset/save. Pass filters, onChange, and filterMenuItems. Returns null when filters.length === 0.
Show code
<FilterChipBar
filters={filters}
onChange={setFilters}
filterMenuItems={[{ value: 'campaign', label: 'Campaign', filterType: 'name' }, ...]}
/>NameFilterDialog
NameFilterDialog
Modal for name-type filters. Options support an optional secondaryLabel rendered inline as a small badge next to the name. Pass virtualizeList for large lists (500+ items) to keep only visible rows in the DOM.
Full / select-only / operator-only control which operators appear. Virtualized demo shows 500 options with inline type badges β only visible rows are in the DOM.
Show code
<NameFilterDialog
open={open}
onClose={() => setOpen(false)}
onApply={(result) => { ...; setOpen(false); }}
title="Campaign(s)"
options={[{ id, label, secondaryLabel? }, ...]}
showSelectOperator={true}
showNonSelectOperators={true}
virtualizeList // opt-in for large lists
/>StatusFilterDialog
StatusFilterDialog
Status selector dialog with configurable single-select (radio) and multi-select (checkbox) modes.
Show code
<StatusFilterDialog
open={open}
onClose={() => setOpen(false)}
onApply={(result) => { ...; setOpen(false); }}
selectionMode="single"
options={[{ id: 'enabled', label: 'Enabled' }]}
/>TableColumnSelector
TableColumnSelector
Panel to show/hide and reorder table columns. open, onOpenChange, onApply(state), columns.
Show code
<TableColumnSelector
columns={[{ id, label, visible }, ...]}
open={open}
onOpenChange={setOpen}
onApply={(state) => setState(state)}
/>FullscreenToggle
FullscreenToggle
Button that fullscreens the element at targetRef. Add FULLSCREEN_TARGET_CLASS to the target for viewport styling.
Show code
<div ref={targetRef} className={FULLSCREEN_TARGET_CLASS}>...</div>
<FullscreenToggle targetRef={targetRef} />DarkModeToggle
DarkModeToggle
Button that toggles light/dark mode. Requires DarkModeProvider from @primitives/context. showLabel, size: sm | md.
Show code
<DarkModeProvider><DarkModeToggle showLabel size="md" /></DarkModeProvider>DateRangePicker β Non-Compare Mode
DateRangePicker β Non-Compare Mode
Calendar popover for selecting a single date range (no comparison). value, onChange, label.
Show code
<DateRangePicker value={range} onChange={setRange} label="Date range" />DateRangePicker β Compare Mode
DateRangePicker β Compare Mode
With comparison enabled, the popover adds a 'Compare to' toggle plus a second calendar for the previous period. comparison, comparisonValue, onComparisonChange.
Show code
<DateRangePicker
value={range}
onChange={setRange}
label="Date range"
comparison
comparisonValue={compare}
onComparisonChange={setCompare}
/>DateRangePicker β Custom Presets
DateRangePicker β Custom Presets
Override the Quick Select sidebar by passing a presets array of { label, getRange }. Useful for limiting choices (e.g. 'Recent Week' / 'Recent Date') or domain-specific shortcuts. When omitted, the standard 5 presets are shown.
Show code
const customPresets: DateRangePresetItem[] = [
{ label: 'Recent Week', getRange: () => ({ start: subDays(new Date(), 6), end: new Date() }) },
{ label: 'Recent Month', getRange: () => ({ start: subDays(new Date(), 29), end: new Date() }) },
{ label: 'Year to Date', getRange: () => ({ start: startOfYear(new Date()), end: new Date() }) },
];
<DateRangePicker value={range} onChange={setRange} label="Date range" presets={customPresets} />DateRangePicker β Fixed Presets (hideCustom)
DateRangePicker β Fixed Presets (hideCustom)
Pass hideCustom to remove the 'Custom' calendar-selection button from the Quick Select sidebar. Use when only specific preset ranges are valid for a page β e.g. Search Term Impressions only supports Recent Week and Recent Date.
Show code
<DateRangePicker
value={range}
onChange={setRange}
label="Date range"
presets={fixedPresets}
hideCustom
/>MetricChip
MetricChip
Compact chip for selected metrics with color dot, chart type toggles, and remove action.
Show code
<MetricChip
label="Spend"
color="#4573D2"
type="line"
onTypeChange={(type) => {}}
onRemove={() => {}}
/>MetricsSelector
MetricsSelector
Multi-select for which metrics to show. metrics, value, onChange, max, min.
Show code
<MetricsSelector metrics={[...]} value={value} onChange={setValue} />ExportToolbar
ExportToolbar
Dropdown with PNG and Excel export. containerRef for PNG, onExportExcel for spreadsheet.
Chart or content area for PNG export
Show code
<ExportToolbar containerRef={ref} filename="chart" onExportExcel={() => {}} />Toggle
Toggle
Switch control. checked, onChange, label, disabled.
Show code
<Toggle checked={checked} onChange={setChecked} label="Enable feature" />TimeAggregation
TimeAggregation
Selector for Daily / Weekly / Monthly. value, onChange.
Show code
<TimeAggregation value={value} onChange={setValue} />RollUpSelector
RollUpSelector
Grouping selector. options, value, onChange.
Show code
<RollUpSelector options={[...]} value={value} onChange={setValue} />FrequencySelector
FrequencySelector
Frequency of tracking with Save. title, options, value, onChange, onSave.
Frequency Of Tracking
Show code
<FrequencySelector title="Frequency" options={[...]} value={value} onChange={setValue} onSave={() => {}} />EntitySelector
EntitySelector
Campaign and Ad Group tree selector. campaigns, selectedCampaignIds, selectedAdGroupIds, onChange.
Show code
<EntitySelector campaigns={[...]} onChange={(c, a) => {}} />ViewModeToggle
Chart / Table Switch
Icon toggle for switching between chart and table views. value, onChange, size.
Show code
<ViewModeToggle value={mode} onChange={setMode} />Size Variants & Disabled
Available in sm and md sizes. Can be disabled.
Show code
<ViewModeToggle value="chart" onChange={...} size="sm" />LayoutSelector
Dashboard Layout Picker
Popover with grid thumbnails for choosing dashboard layout. options, value, onChange.
Show code
<LayoutSelector options={layouts} value={layout} onChange={setLayout} />DataLabelsToggle
Data Labels Toggle
Toggle button to show/hide data labels on charts. pressed, onPressedChange, size, disabled.
Show code
<DataLabelsToggle pressed={on} onPressedChange={setOn} />DataExportButton
Data Export Button
Downloads table data as CSV. getData (async), columns, fileName, visibleColumnIds.
Show code
<DataExportButton getData={async () => rows} columns={columns} fileName="export" />TableToolbar
TableToolbar
Generic table toolbar composing column-selector, export, fullscreen, and chart-toggle actions. All slots are optional. Pass exportSlot for custom export buttons with toasts.
Show code
<TableToolbar
featureToolbar={<span className="text-sm text-iw-text-secondary">Custom actions here</span>}
chartToggle={{ showChart, onToggleChart: () => setShowChart(v => !v) }}
showColumnSelector
onColumnSelectorOpen={() => alert('Open column selector')}
showExport
exportProps={{ getData: async () => rows, columns, fileName: 'export' }}
showFullscreen
fullscreenExpanded={fullscreenExpanded}
onFullscreenChange={setFullscreenExpanded}
targetRef={containerRef}
/>Card
Basic Card
Generic surface container with optional header slot.
Simple card content
Card with header slot
Show code
<Card>Content goes here</Card>
<Card header={<span>Header</span>}>With header slot</Card>InputField
Sizes & States
InputField supports sm, md, lg sizes with error, disabled, and readOnly states.
This field is required
Show code
<InputField label="Name" size="md" value={value} onChange={setValue} />
<InputField label="Error" error="Required" value="" onChange={() => {}} />
<InputField label="Disabled" disabled value="Cannot edit" onChange={() => {}} />FileUpload
Image Upload (300Γ180, β€200 KB)
Image variant renders an inline thumbnail. Mirrors the Sponsored Brand logo upload constraints in Walmart Add Campaign (300Γ180 px, max 200 KB).
Drag and drop, or
PNG or JPG. Exactly 300Γ180 px, up to 200 KB.
Show code
<FileUpload
variant="image"
accept="image/*"
value={file}
onChange={setFile}
validate={validateImageDimensions({ width: 300, height: 180 })}
maxSizeBytes={200 * 1024}
label="Brand logo"
/>Video Upload (1920Γ1080 or 3840Γ2160, 5β30s, β€500 MB)
Video variant renders an inline player and chains dimension + duration validators. Mirrors the Sponsored Video constraints in Walmart Add Campaign.
Drag and drop, or
MP4. 1920Γ1080 or 3840Γ2160 px, 5β30 seconds, up to 500 MB.
Show code
<FileUpload
variant="video"
accept="video/*"
value={file}
onChange={setFile}
validate={[
validateVideoDimensions([{ width: 1920, height: 1080 }, { width: 3840, height: 2160 }]),
validateVideoDuration({ minSec: 5, maxSec: 30 }),
]}
maxSizeBytes={500 * 1024 * 1024}
label="Sponsored video"
/>Generic File (.srt captions)
Generic variant renders just the filename + size. No additional validators β accept restricts the picker.
Drag and drop, or
SubRip captions (.srt) attached to the sponsored video.
Show code
<FileUpload
variant="generic"
accept=".srt"
value={file}
onChange={setFile}
label="Captions"
/>Button
Primary, Secondary, and Tertiary Buttons
Primary and secondary buttons support optional icons. Tertiary is text-forward for low-emphasis actions.
Show code
<Button variant="primary" startIcon={<Plus />}>New Rule</Button>
<Button variant="secondary">View History</Button>
<Button variant="tertiary">Cancel</Button>SelectField
Select Field
Native select with boxed and underline variants using primitives theme tokens.
Show code
<SelectField value={value} onChange={setValue} label="Target">
<option value="line_items">Line Items</option>
<option value="ad_groups">Ad Groups</option>
</SelectField>CheckboxField
Checkbox Field
Checkbox states aligned to the primitive visual system. The label prop accepts a plain string or any ReactNode β useful for inline badges next to a name.
Show code
<CheckboxField checked={checked} onChange={setChecked} label="Enable undo schedule" />
// ReactNode label β name + inline badge
<CheckboxField
checked={checked}
onChange={setChecked}
label={
<span className="inline-flex items-baseline gap-1.5">
<span>Enable undo schedule</span>
<span className="text-[10px] font-medium uppercase tracking-wide text-iw-text-muted">beta</span>
</span>
}
/>RadioField
Radio Field
Radio states aligned to primitive tokens with selected and disabled visuals.
Show code
<RadioField checked={value === 'a'} onChange={(checked) => checked && setValue('a')} />TimeField
Time Field
Time input control with the same primitive visual system as other form controls.
Show code
<TimeField value={value} onChange={setValue} label="Execution Time" />Toast
Toast Notifications
Three design options β Accent, Solid, and Soft β across success, info, warning, failure (error), and abort intents. Pick a design below and trigger live toasts; pass it via the design option.
White surface, left colour accent + tinted icon chip.
Fully filled in the intent colour, white text.
Light tinted background with a matching border.
Show code
const { toasts, addToast, removeToast } = useToast();
// design: 'accent' (default) | 'solid' | 'soft'
addToast('success', 'Your automation rule is now live.', { title: 'Rule saved', design: 'solid' });
addToast('error', 'We couldnβt save the rule.', { title: 'Save failed', design: 'soft' });
// or set one design for every toast on the container:
<ToastContainer toasts={toasts} onRemove={removeToast} design="accent" />DateTimePicker
Date & Time Picker
Single date-time picker and date-only mode (time disabled), including underline variant.
Datetime: 2026-04-07T14:30:00Z
Date only: 2026-04-07
Show code
<DateTimePicker value={dateTime} onChange={setDateTime} label="Start Date" />
<DateTimePicker value={dateOnly} onChange={setDateOnly} showTime={false} variant="underline" label="Execution Date" />CalendarRangeModal
Calendar Range Modal
Modal calendar for selecting a date range.
Show code
<CalendarRangeModal
isOpen={open}
onClose={() => setOpen(false)}
initialStartDate={start}
initialEndDate={end}
onApply={(s, e) => { setStart(s); setEnd(e); setOpen(false); }}
/>DateRangeSelector
Date Range Selector
Preset dropdown with optional custom range support.
Preset: last7 | Range: 2026-03-31 to 2026-04-07
Show code
<DateRangeSelector
selectedPreset={preset}
onPresetChange={setPreset}
customRange={{ startDate: start, endDate: end }}
onCustomRangeChange={(s, e) => { setStart(s); setEnd(e); }}
/>Accordion
Single Expand
Only one item can be open at a time. Supports badges and disabled items.
Expanded: none
Show code
<Accordion items={items} expanded={expanded} onExpandedChange={setExpanded} />Multiple Expand
Multiple items can be expanded simultaneously.
Show code
<Accordion items={items} allowMultiple />Checkbox
Checkbox States & Sizes
Standalone checkbox with sizes, indeterminate state, and optional label.
sm
md
lg
indeterminate
disabled
Show code
<Checkbox checked={checked} onCheckedChange={setChecked} label="Enable" size="md" />FilterConditionCell
With Conditions
Toggle to show/hide filter criteria. Designed for use inside table cells.
Show code
<FilterConditionCell conditions={[{ fieldLabel: 'Campaign', opLabel: 'is', valueStr: 'SP' }]} />Empty State
Renders an em-dash when no conditions are present.
Show code
<FilterConditionCell conditions={[]} />SearchInput
Search Input
Text input with search icon, optional clear button, and debounce support.
Value: (empty)
Show code
<SearchInput value={value} onChange={setValue} onSearch={handleSearch} debounceMs={300} />SelectableTable
Interactive Selection
Table with checkbox column, header select-all, and sortable columns.
| Campaign | Status | Spend | ROAS | |
|---|---|---|---|---|
| Brand Defence β SP | Active | $1,240.50 | 4.2x | |
| Category Conquest β SB | Active | $890.30 | 3.1x | |
| Retargeting β SD | Paused | $320.00 | 2.8x | |
| New Launch β SP | Active | $2,100.75 | 5.6x | |
| Competitor ASIN β SP | Ended | $450.20 | 1.9x |
0 of 5 selected
Show code
<SelectableTable columns={cols} rows={rows} rowIdField="id"
selectedIds={selectedIds} onSelectionChange={setSelectedIds} />Radio (single-select)
selectionMode='radio' replaces checkboxes with radio buttons β selecting a row deselects all others. Useful for picking one item from a list.
| Campaign | Status | Spend | ROAS | |
|---|---|---|---|---|
| Brand Defence β SP | Active | $1,240.50 | 4.2x | |
| Category Conquest β SB | Active | $890.30 | 3.1x | |
| Retargeting β SD | Paused | $320.00 | 2.8x | |
| New Launch β SP | Active | $2,100.75 | 5.6x | |
| Competitor ASIN β SP | Ended | $450.20 | 1.9x |
Selected: none
Show code
<SelectableTable
selectionMode="radio"
columns={cols} rows={rows} rowIdField="id"
selectedIds={selectedIds} onSelectionChange={setSelectedIds} />Loading State
Shows loading overlay while data is being fetched.
| Campaign | Status | Spend | ROAS | |
|---|---|---|---|---|
| Loading... | ||||
Show code
<SelectableTable loading columns={cols} rows={[]} ... />Row Height Constraints
rowMinHeight and rowMaxHeight set a floor/ceiling on each row. Useful when cells contain variable-height content like tags or wrapped text.
| Campaign | Status | Spend | ROAS | |
|---|---|---|---|---|
| Brand Defence β SP | Active | $1,240.50 | 4.2x | |
| Category Conquest β SB | Active | $890.30 | 3.1x | |
| Retargeting β SD | Paused | $320.00 | 2.8x | |
| New Launch β SP | Active | $2,100.75 | 5.6x | |
| Competitor ASIN β SP | Ended | $450.20 | 1.9x |
Show code
<SelectableTable
rowMinHeight="48px"
rowMaxHeight="72px"
columns={cols} rows={rows} rowIdField="id"
selectedIds={selectedIds} onSelectionChange={setSelectedIds} />TransparentCard
Transparent vs Regular Card
TransparentCard removes border, background, and shadow from the base Card component.
Has border, background, and shadow.
No border, no background, no shadow.
Show code
<Card header="Regular">content</Card>
<TransparentCard header="Transparent">content</TransparentCard>TreeSelect
Basic Tree
Two-level checkbox tree with parent indeterminate states and selection counts.
Selected: none
Show code
<TreeSelect items={items} selectedIds={selectedIds} onSelectionChange={setSelectedIds} />With Search
Searchable mode filters both parent and child labels.
Show code
<TreeSelect items={items} selectedIds={selectedIds} onSelectionChange={setSelectedIds} searchable />RadioGroup
Horizontal Orientation
Inline radio group for simple binary or small-set choices (e.g. Include/Exclude targeting).
Selected: include
Show code
<RadioGroup
options={[{ value: 'include', label: 'Include' }, { value: 'exclude', label: 'Exclude' }]}
value={value}
onChange={setValue}
orientation="horizontal"
/>Vertical Orientation
Stacked radio group for longer option lists (e.g. bid edit type selector).
Selected: new-bid
Show code
<RadioGroup
options={[{ value: 'new-bid', label: 'Set New Bid' }, ...]}
value={value}
onChange={setValue}
orientation="vertical"
/>Disabled States
Individual options or the entire group can be disabled.
Single option disabled
Entire group disabled
Show code
<RadioGroup options={[...]} value="active" disabled />Custom Option Label Class
optionLabelClassName applies extra Tailwind classes to every option label span β useful for muted, italic, or otherwise styled labels.
Show code
<RadioGroup
options={[{ value: 'set', label: 'Set new bid' }, ...]}
value={value}
onChange={setValue}
orientation="vertical"
optionLabelClassName="italic text-iw-text-muted"
/>IWDialog
Default Dialog
Modal dialog with header (auto close button), scrollable body, and footer with action buttons.
Show code
<IWDialog isOpen={open} onClose={() => setOpen(false)}>
<IWDialogHeader title="Edit Line Item" />
<IWDialogBody>...</IWDialogBody>
<IWDialogFooter>
<Button variant="secondary" onClick={onClose}>Cancel</Button>
<Button variant="primary" onClick={onSave}>Save</Button>
</IWDialogFooter>
</IWDialog>Branded Header
Use variant='branded' for a primary-colored header (e.g. bulk edit confirmation).
Show code
<IWDialogHeader title="Bulk Update" variant="branded" />Size Variants
Sizes: sm (400px), md (560px), lg (760px), xl (960px), full (95vw).
Show code
<IWDialog isOpen={open} onClose={onClose} size="xl">...</IWDialog>IWDrawer
Right Drawer (default)
Slide-in panel from the right edge. Supports header, scrollable body, and footer.
Show code
<IWDrawer isOpen={open} onClose={() => setOpen(false)}>
<IWDrawerHeader title="Settings" />
<IWDrawerBody>...</IWDrawerBody>
<IWDrawerFooter>
<Button variant="primary" onClick={onClose}>Apply</Button>
</IWDrawerFooter>
</IWDrawer>Left Drawer
Slides in from the left edge. Useful for navigation or filter panels.
Show code
<IWDrawer isOpen={open} onClose={onClose} position="left" width="20rem">...</IWDrawer>MetricFilterDialog
Number Filter
Filter by numeric metrics with operators: equal, less than, greater than, between, etc.
Show code
<MetricFilterDialog
open={open}
onClose={() => setOpen(false)}
title="Filter by Impressions"
filterType="number"
onApply={(result) => console.log(result)}
/>Text Filter
Filter by text with operators: equals, contains, does not contain, not equals.
Show code
<MetricFilterDialog
open={open}
onClose={() => setOpen(false)}
title="Filter by Campaign Name"
filterType="text"
onApply={(result) => console.log(result)}
/>CopyButton
Default (always visible)
Standalone copy button. Click to copy the text; the icon briefly becomes a checkmark.
Show code
<CopyButton text="Campaign name test99673" />With onCopy callback
Fires onCopy after a successful write β use this to trigger a page-level toast.
Show code
<CopyButton
text="some-api-key-123"
label="Copy API key"
onCopy={(text) => console.log('Copied:', text)}
/>Hover-reveal (table cell usage)
Add opacity-0 group-hover:opacity-100 via className β the parent must have the 'group' class. This is how EntityNameCell uses it.
Hover the box above to reveal the icon.
Show code
<div className="group flex items-center gap-1 w-48 border rounded p-2">
<span className="flex-1 truncate text-sm">Campaign name test99673</span>
<CopyButton
text="Campaign name test99673"
className="opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
/>
</div>