Skizo

Skizo is a small language to sketch user interfaces. You write one Element on each line. The skizo command checks the file, shows it as a Preview page and exports it as shadcn React code.

Screen "Orders" subtitle="Track customer orders"actionsButton "New order" icon=plus opens=newTable cols="Order,Customer,Amount" rows=3Dialog "New order" id=newInput "Customer"footerButton "Create"
  • Short source. Each line holds one Element. Skizo has no closing tags and no styles.
  • One way to write each thing. Skizo has 33 Element types. skizo check rejects all other types.
  • All errors in one run. skizo check shows each error with a hint.
  • Code that you keep. The export uses the shadcn components of your project.
  • Phones and tablets. One sketch fits each screen size. The Preview shows each width.

Get started

Requirements

  • Node.js 22.12 or later.
  • Git.
  • pnpm (recommended) or npm.

Install

Skizo installs from its Git repository. Each release has a Git tag, for example v0.4.0-alpha.1. Always put the tag in the install command. Then each install gets the same release.

Install with pnpm (recommended):

pnpm add -g github:jctosta/skizo#v0.4.0-alpha.1

Or install with npm:

npm install -g --allow-git=all github:jctosta/skizo#v0.4.0-alpha.1

npm 12 and later blocks Git dependencies by default. The --allow-git=all option allows them for this command only.

Make sure that the install is correct:

skizo --help

The install gives two commands: skizo and its short alias skz.

Pin an exact build

A release tag points to a commit on the release branch. This commit holds only the built CLI. It has no build scripts and no dependencies. Thus the install runs no code from the package.

A tag can move to a different commit. To pin one exact build, use the commit hash instead of the tag.

  1. Get the commit hash of the release:
    git ls-remote https://github.com/jctosta/skizo "refs/tags/v0.4.0-alpha.1^{}"
  2. Install that commit:
    pnpm add -g github:jctosta/skizo#<commit-hash>

Upgrade and remove

To upgrade, run the install command again with the new tag. To remove Skizo, run one of these commands:

pnpm remove -g @jctosta/skizonpm uninstall -g @jctosta/skizo

Write your first sketch

  1. Save this text as orders.skz:
    Screen "Orders" subtitle="Track customer orders"actionsButton "New order" icon=plusCard "Revenue" desc="Last 30 days"Stat "$12,450" hint="+12% vs last month" trend=upTable cols="Order,Customer,Amount" rows=5
  2. Check the file. The command shows all errors in one run, each with a hint.
    skizo check orders.skz
  3. Make the Preview. The command writes orders.html. Open this file in a browser.
    skizo preview orders.skz
  4. To see your changes while you edit, serve the Preview. The page reloads each time that you save.
    skizo preview orders.skz --watch
  5. Export the Screens as React code:
    skizo export orders.skz --target shadcn --out src/screens

Build from source

To change Skizo, build it from a clone of the repository. The build needs Node.js 22.18 or later.

git clone https://github.com/jctosta/skizocd skizopnpm installpnpm buildnode packages/cli/dist/cli.mjs --help

Use Skizo with agents

Coding agents such as Claude Code, Codex and Cursor write Skizo. You look at the Preview and tell the agent what to change.

The command skizo guide prints the Guide. The Guide holds the full language in about 1,300 tokens. An agent needs no other document.

Add these lines to the instructions for your agent, for example in AGENTS.md or CLAUDE.md:

Before you write UI code, make a sketch in Skizo:1. Run `skizo guide` and read the Guide.2. Write the sketch in <name>.skz.3. Run `skizo check <name>.skz`. Fix all errors. Do this step again until the command shows no errors.4. Run `skizo preview <name>.skz` and give me the HTML file.

skizo check shows each error with its line, its Element type and a hint. Thus the agent can fix all errors in one pass:

$ skizo check orders.skzorders.skz:4  Button  unknown prop "varient" — did you mean variant?orders.skz:7  Badge  unknown flag "secondary" — did you mean variant=secondary?orders.skz:9  Table  row has 3 cells, expected 4 (cols: Order, Customer, Status, Amount)

While the agent works, keep skizo preview --watch open. The page reloads after each save that passes check. You can correct the sketch before somebody writes the real code.

Syntax

Lines and indentation

Write one Element on each line. Indent each level of nesting by two spaces. Do not use tabs. A child can be only one level deeper than its parent. A file has one or more Screens at the top level.

Screen "Settings"Card "Profile"Input "Display name"

Labels, flags and props

A line starts with the Element type. After the type, write an optional Label, flags and props, in any order. The Label is a quoted string. Its meaning depends on the type. For example, it is the text of a Button, the value of a Stat and the alt text of an Image.

  • A flag is a lowercase word without a value. It means true. Examples are disabled, open and icon-only.
  • A prop has the form key=value. Put quotes around a value with spaces: placeholder="Jane Doe".
  • In a quoted value, \" is a quote and \\ is a backslash.
  • Only flags are bare words. Write variant=secondary. Do not write secondary alone.
  • A list prop has values separated by commas: options="Open,Paid". Write \, for a comma in a value.
Button "Save" variant=secondary icon=check disabled

Text lines, table rows and comments

A line with only a string is a paragraph. It is the same as a Text. A line that starts with | is a row of a Table or a Chart. Each row has one cell for each column. A line that starts with // is a comment. The Preview and the export ignore comments.

// A note for the human. Skizo does not show it."A paragraph of text."Table cols="Name,Role"| Ana Souza | Admin| Bruno Lima | Member

Slots

A slot word puts children into one part of their parent. Write the slot word on its own line. Then indent the children below it.

  • actions puts the children in the header of a Screen or a Card.
  • footer puts the children in the footer of a Card, a Dialog or a Sheet.
Card "Invoice"actionsButton "Download" icon=download icon-only variant=ghostText "Due on March 3"footerButton "Pay now"

Ids

All Elements accept id=name. An id is the target of opens= or to=. Each id must be unique in the file. If a Screen has no id, Skizo makes one from its Label. For example, Order details becomes order-details.

One content source

Give the content in one way only:

  • A Text has a Label or lorem=N.
  • A Table has | rows or rows=N.
  • A Chart has | rows or rows=N. It must have one of the two.
  • A List has items= or rows=N.

lorem=N and rows=N make placeholder content. The placeholder content is the same each time that you run Skizo.

Icons

The icon= prop takes the name of a Lucide icon in kebab case, for example plus, house or trash-2. If the name is not correct, skizo check shows the nearest name.

Behavior

Behavior is the effect of a click that you write in the source. Skizo has two kinds of Behavior:

  • opens=id on a Button shows the Dialog or Sheet with that id. The Dialog or Sheet must be on the same Screen.
  • to=screen-id on a Button or a Sidebar Link shows a different Screen of the file.

All other interactions work without Behavior:

  • A Tab shows its panel when you click it.
  • A Menu or a Select opens when you click it.
  • A Dialog or a Sheet closes from its close button, its backdrop or the Escape key. A footer Button without Behavior also closes it.

Form controls do not validate or send data.

Screen "Projects" id=projectsactionsButton "New project" icon=plus opens=new-projectButton "Open settings" variant=link to=settingsDialog "New project" id=new-projectInput "Name"footerButton "Create" Screen "Settings" id=settingsButton "Back to projects" variant=outline to=projects

The Preview shows one Screen at a time. The URL hash gives the name of the Screen. Thus the back button of the browser works. If the file has no to=, the Preview shows all Screens, one below the other.

Phones and tablets

Each sketch fits phones, tablets and desktops. Write one layout, and Skizo changes it for each width. On a phone (narrower than 768 px), a Grid shows one column. On a tablet (768 px to 1023 px), a Grid shows a maximum of two columns. A Row puts the children that do not fit on a new line. A Table scrolls horizontally. A Screen has less padding on a phone. On a phone, the Sidebar is hidden, and a button with the menu icon opens it from the left. The Preview and the export use the same Tailwind classes: md: for tablets and lg: for desktops.

This image shows the Orders example at desktop width and at phone width:

The Preview of the Orders Screen at desktop width and at phone width. On the desktop, a Sidebar is on the left and three cards are in one row. On the phone, a menu button replaces the Sidebar and the cards are in one column.

The toolbar at the top of the Preview sets the width of the sketch: Desktop (1280 px), Tablet (768 px) or Phone (390 px). If the window is narrower than the width, the Preview makes the sketch smaller to fit the window. To open the Preview at a width, use --viewport:

skizo preview orders.skz --viewport phone

With --watch, the page keeps the width and the Screen after each reload.

CLI reference

CommandResult
skizo guidePrints the Guide. The Guide is the language reference for agents.
skizo check <file.skz> [--json]Shows all errors in the file, one on each line, in the format file:line Type message. With --json, the command prints {"errors": [{"file", "line", "type", "message"}]}.
skizo preview <file.skz> [--watch] [--viewport desktop|tablet|phone]Writes <file>.html next to the source file. The page holds all Screens and works offline. With --watch, the command also serves the page on 127.0.0.1 and reloads it after each save. If a save fails check, the command shows the errors and keeps the last good page. --viewport sets the width at which the page opens. The default is desktop.
skizo export <file.skz> --target shadcn [--out dir] [--router next|react-router]Writes one .tsx file for each Screen, and app-sidebar.tsx for a shared Sidebar. The files go next to the source file, or into the --out folder. With --router, the links use the Link component of Next.js or React Router.

skz is a short alias for skizo. preview and export do not accept a file that fails check. They show the same errors as check.

The exit code tells the result:

  • 0 means that the command was successful.
  • 1 means that the file has errors.
  • 2 means that the command is not correct, or that Skizo cannot read the file.

shadcn export

skizo export --target shadcn writes React code. The code imports the components of your project from @/components/ui/*. Thus the export uses your theme and your changes to the components. Each Screen becomes one file with the name of its id. For example, orders.tsx exports OrdersScreen.

Requirements

  • A shadcn project on the Radix base. The export uses asChild.
  • The lucide-react package.
  • The shadcn components that the file uses: alert, avatar, badge, button, card, chart, checkbox, dialog, dropdown-menu, input, label, progress, radio-group, select, separator, sheet, switch, table, tabs and textarea.
  • The recharts package, if the file has a Chart. The shadcn chart component installs it.
  • For each Custom name, your component in @/components/<kebab-name>. For example, ChatBubble comes from @/components/chat-bubble.

Output

  • opens= becomes a useState hook. A click on the Button sets the state, and the state opens the Dialog or Sheet. The open flag sets the initial state to open.
  • to= becomes a plain <a href="/screen-id"> link. A Button with to= wraps the link with asChild.
  • --router next writes each link as <Link href="/screen-id"> and imports Link from next/link. A file with useState or a Chart starts with "use client". Then the App Router runs it in the browser.
  • --router react-router writes each link as <Link to="/screen-id"> and imports { Link } from react-router.
  • A shared Sidebar becomes one file, app-sidebar.tsx. It exports AppSidebar({ current }). Each Screen that shows the Sidebar renders <AppSidebar current="screen-id" />.
  • An export of an unchanged file writes the same bytes. Thus a diff shows only the changes that you made in the source.

Elements

Each Element type has an example and its live Preview. The prop tables come from the schemas that skizo check uses. In a list of values, the first value is the default. All Elements also accept id=.

Screens and navigation

Screen

One page of the app that you sketch. A file has one or more Screens at the top level. All other Elements go in a Screen.

Screen "Orders" subtitle="Manage and track customer orders"actionsButton "Export" variant=outline icon=downloadButton "New order" icon=plusText "The body puts its children in a column with a medium gap."
Label
Required. The page title in the header row.
Children
Any Elements
Position
The top level of the file
Slots
actions
PropValuesDefaultDescription
id"text"The target of to=. If you do not write it, Skizo makes it from the Label.
subtitle"text"A muted line below the title.
plainflagShows no shared Sidebar on this Screen, for example on a sign-in Screen.
  • The actions slot puts Elements on the right side of the header row.

Export: A <main> element with a header row for the title, the subtitle and the actions.

Layout

Stack / Row

A Stack puts its children from top to bottom. A Row puts its children from left to right.

Stack gap=smText "A Stack puts its children from top to bottom."Row gap=sm justify=between align=centerBadge "Row"Text "A Row puts them from left to right." mutedButton "Action" size=sm variant=outline

Stack

Label
None
Children
Any Elements
Position
Anywhere in a Screen
PropValuesDefaultDescription
gapmd | sm | lgmdThe space between the children. sm, md and lg give Tailwind gap-2, gap-4 and gap-6.
alignstart | center | endstartHow the children align across the Stack or Row. With start, the children stretch.
justifystart | between | endstartHow the children spread along the Stack or Row.

Row

Label
None
Children
Any Elements
Position
Anywhere in a Screen
PropValuesDefaultDescription
gapmd | sm | lgmdThe space between the children. sm, md and lg give Tailwind gap-2, gap-4 and gap-6.
alignstart | center | endstartHow the children align across the Stack or Row. With start, the children stretch.
justifystart | between | endstartHow the children spread along the Stack or Row.

Grid

Columns of equal width. A direct child can span more than one column with span=N.

Grid cols=3Card "Revenue"Stat "$12,450"Card "Orders"Stat "318"Card "Refunds"Stat "4"Card "Traffic" span=2Chart "Daily visits" type=area cols="Day,Visits" rows=7Card "Notes"Text lorem=14
Label
None
Children
Any Elements
Position
Anywhere in a Screen
PropValuesDefaultDescription
cols2 to 62The number of columns.
gapmd | sm | lgmdThe space between the children. sm, md and lg give Tailwind gap-2, gap-4 and gap-6.
span1 or moreWrite it on a direct child, not on the Grid. The child spans this number of columns. The maximum is cols.

Separator

A horizontal line between groups of content.

Text "Above the line"SeparatorText "Below the line"
Label
None
Children
None
Position
Anywhere in a Screen

Card

A box with a border. It has an optional title and description, an actions slot in its header and a footer slot.

Card "Team plan" desc="Billed yearly"actionsBadge "Popular" variant=secondaryStat "$24" hint="per seat per month"footerButton "Upgrade"
Label
Optional. The title.
Children
Any Elements
Position
Anywhere in a Screen
Slots
actions, footer
PropValuesDefaultDescription
desc"text"A description below the title.

Export: Card > CardHeader(CardTitle, CardDescription, CardAction) + CardContent + CardFooter.

Tabs / Tab

Panels that you select with tabs. The children of a Tab are its panel. In the Preview, a click on a tab shows its panel.

TabsTab "Overview"Text "The first Tab is active if no other Tab has the active flag."Tab "Activity"List items="Paid #1042,Refunded #1039,Paid #1038"Tab "Settings"Switch "Email alerts" checked

Tabs

Label
None
Children
Tab
Position
Anywhere in a Screen
Rule
Only one child can have active. If no child has it, the first child gets it.

Tab

Label
Required. The tab title.
Children
Any Elements
Position
Directly in Tabs
PropValuesDefaultDescription
activeflagMarks the current item.
  • Tabs can contain only Tabs. A Tab can only be in Tabs.

Export: Tabs defaultValue > TabsList(TabsTrigger*) + TabsContent*.

Content

Heading

A section heading. The default level is 2.

Heading "Level 1" level=1Heading "Level 2 (default)"Heading "Level 3" level=3Heading "Level 4" level=4
Label
Required. The heading text.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
level2 | 1 | 3 | 42The heading level, h1 to h4.

Text

A paragraph. A line with only a string is also a Text.

Text "Body text. The default size is md."Text "Small and muted text for less important information." size=sm mutedText "Bold text." bold"A line with only a string is a Text."Text lorem=24
Label
Optional. The text. Write a Label or lorem=, not the two.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
sizemd | sm | lgmdThe size of the Element.
mutedflagUses the muted color.
boldflagUses bold text.
lorem1 or moreShows N placeholder words instead of a Label.

Stat

A key number with an optional title and hint. trend sets the color of the hint and adds an arrow.

Grid cols=3Stat "$12,450" title="Revenue" hint="+12% vs last month" trend=upStat "3.4%" title="Conversion" hint="-0.3% vs last month" trend=downStat "318" title="Orders"
Label
Required. The value.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
title"text"A small heading above the value.
hint"text"A small line below the value.
trendnone | up | downnoneThe direction of the change. up is green and down is red.

Export: Plain markup for the title, the value and the hint. shadcn has no Stat component.

Badge

A small status label.

Row gap=smBadge "Default"Badge "Secondary" variant=secondaryBadge "Outline" variant=outlineBadge "Destructive" variant=destructive
Label
Required. The badge text.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
variantdefault | secondary | outline | destructivedefaultThe visual style.

Avatar

The initials of a person in a circle.

Row gap=sm align=centerAvatar "Ana Souza" size=smAvatar "Bruno Lima"Avatar "Grace Kim" size=lg
Label
Required. The name of the person. The Avatar shows the initials. The full name is the accessible name.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
sizemd | sm | lgmdThe size of the Element.

Image

A placeholder box for a picture, a map or a video. For a chart, use Chart.

Grid cols=3Image "Product photo" ratio=1:1Image "Hero banner"Image "Store map" ratio=4:3
Label
Required. The alt text. The box shows it.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
ratioW:H16:9The width-to-height ratio in whole numbers, for example 16:9, 4:3 or 21:9.

Alert

A message box with a title and optional text.

Alert "Heads up" text="Your trial ends in 3 days."Alert "Payment failed" text="Update your card to keep your plan." variant=destructive
Label
Required. The title.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
text"text"The text below the title.
variantdefault | destructivedefaultThe visual style.

Progress

A progress bar.

Text "Uploading 3 of 5 files" size=smProgress value=60
Label
None
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
value0 to 100The percentage that is complete. If you do not write it, the value is 0.

Table

A table. Write the rows as | lines, or use rows=N to get placeholder rows.

Table cols="Order,Customer,Status,Amount"| #1042 | Ana Souza | Paid | $120.00| #1041 | Bruno Lima | Open | $64.50Table cols="Name,Email,Date,Status,Amount" rows=3
Label
None
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
cols"a,b,c"The column headers, separated by commas. This prop is necessary.
rows1 or moreShows N placeholder rows instead of | lines.
  • Each | row must have one cell for each column.
  • Skizo makes placeholder cells from the column name: Name, Email, Date, Status or Amount. Other columns get Item n. The cells are the same each time.

Export: Table > TableHeader/TableRow/TableHead* + TableBody/TableRow/TableCell*.

List

A list with bullets. Write the items with items=, or use rows=N to get placeholder items.

List items="Organic search,Direct,Referral"List rows=3
Label
None
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
items"a,b,c"The items, separated by commas.
rows1 or moreShows N placeholder items instead of items=.

Chart

A line, bar, area or pie chart. Write the data as | rows, as in a Table, or use rows=N to get placeholder rows.

GridChart "Revenue" cols="Month,Revenue,Costs"| Jan | 1200 | 800| Feb | 1500 | 950| Mar | 1350 | 1100Chart "Orders" type=bar cols="Day,Orders" rows=5Chart "Traffic" type=area cols="Date,Visits" rows=6Chart "Plans" type=pie cols="Plan,Users"| Free | 620| Pro | 240| Team | 90
Label
Required. The accessible name of the chart. The chart does not show it.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
typeline | bar | area | pielineThe kind of chart.
cols"a,b,c"The column headers, separated by commas. The first column gives the x axis, or the slice names of a pie. Each other column is a series of numbers. This prop is necessary.
rows1 or moreShows N placeholder rows instead of | lines.
  • Each | row must have one cell for each column. The cells after the first cell are numbers, for example 1200 or 12.5.
  • A pie has exactly two columns: the slice name and its value.
  • A pie shows a legend with the names of its slices. Other charts show a legend when they have two or more series.
  • Skizo makes placeholder x values from the column name: Month, Day, Date or Year. Other columns get Item n. The numbers are the same each time.
  • The Preview draws the chart as an SVG image in the default shadcn chart colors. The export uses --chart-1 to --chart-5 of your theme.

Export: ChartContainer > LineChart, BarChart, AreaChart or PieChart from recharts, with ChartTooltip, and with ChartLegend when the Preview shows a legend. The data is a const array above the component, with the column slugs as keys. A ChartConfig gives the labels and colors of the series, or of the slices of a pie.

Actions and forms

Button

A button. With opens=, it shows a Dialog or a Sheet. With to=, it shows a different Screen.

Row gap=smButton "Default"Button "Secondary" variant=secondaryButton "Outline" variant=outlineButton "Ghost" variant=ghostButton "Destructive" variant=destructiveButton "Link" variant=linkRow gap=sm align=centerButton "Small" size=smButton "Large" size=lgButton "New" icon=plusButton "Settings" icon=settings icon-only variant=outlineButton "Saving" loadingButton "Disabled" disabled
Label
Required. The button text. It is necessary also with icon-only.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
variantdefault | secondary | outline | ghost | destructive | linkdefaultThe visual style.
sizemd | sm | lgmdThe size of the Element.
iconLucide icon nameAn icon before the Label.
icon-onlyflagShows only the icon. The Label stays as the accessible name.
disabledflagShows the Button as not available. With to=, the Button has no link.
loadingflagShows a spinner and disables the Button.
opensDialog or Sheet idShows the Dialog or Sheet with this id on the same Screen.
toScreen idShows the Screen with this id. A Button can have opens= or to=, not the two.

Input

A text field with a label.

Input "Email" type=email placeholder="you@example.com"Input "Password" type=password value="hunter2" error="Must be at least 8 characters"Input "Search" type=search placeholder="Search..." disabled
Label
Required. The field label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
typetext | email | password | number | searchtextThe type of field.
placeholder"text"The hint text in the empty field.
value"text"The current value.
error"text"An error message below the field.
disabledflagShows the Element as not available.

Textarea

A text field with more than one line, and a label.

Textarea "Message" placeholder="Write something..." rows=3
Label
Required. The field label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
placeholder"text"The hint text in the empty field.
value"text"The current value.
rows1 or moreThe number of visible lines.

Select

A dropdown list with a label. In the Preview, it opens when you click it.

Select "Status" options="Open,Paid,Refunded" value=PaidSelect "Country" options="Brazil,Portugal,Spain" placeholder="Pick a country"
Label
Required. The field label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
options"a,b,c"The choices, separated by commas. Write \, for a comma in a choice.
value"text"The selected option. It must be one of the options.
placeholder"text"The hint text in the empty field.
openflagShows the Element open when the Preview loads. The export also renders it open first.

Export: Label + Select > SelectTrigger(SelectValue) > SelectContent > SelectItem*.

Checkbox / Switch

Controls with a label that are on or off.

Checkbox "Accept the terms" checkedCheckbox "Subscribe to the newsletter"Switch "Email alerts" checkedSwitch "Beta features" disabled

Checkbox

Label
Required. The control label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
checkedflagTurns the control on.
disabledflagShows the Element as not available.

Switch

Label
Required. The control label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
checkedflagTurns the control on.
disabledflagShows the Element as not available.

Radio

A group of options with a label. The user can select only one option.

Radio "Plan" options="Free,Pro,Team" value=Pro
Label
Required. The group label.
Children
None
Position
Anywhere in a Screen
PropValuesDefaultDescription
options"a,b,c"The choices, separated by commas. Write \, for a comma in a choice.
value"text"The selected option. It must be one of the options.

Overlays

Dialog

A modal window. A Button with opens= shows it. The open flag shows it when the Preview loads. It must be a direct child of its Screen.

RowButton "Edit profile" opens=edit-profileDialog "Edit profile" id=edit-profile desc="Click Save to keep your changes." openInput "Name" value="Ana Souza"footerButton "Cancel" variant=outlineButton "Save"
Label
Required. The dialog title.
Children
Any Elements
Position
Directly in Screen
Slots
footer
PropValuesDefaultDescription
desc"text"A description below the title.
openflagShows the Element open when the Preview loads. The export also renders it open first.
  • These close the Dialog: a footer Button without Behavior, the close button, the backdrop and the Escape key.

Export: Dialog > DialogContent > DialogHeader(Title, Description) + body + DialogFooter. The export puts each footer Button in a DialogClose.

Sheet

A modal window at the left or right side of the Screen. The rules for a Dialog also apply to a Sheet.

RowButton "Filters" icon=sliders-horizontal variant=outline opens=filtersSheet "Filters" id=filters desc="Show fewer orders." openSelect "Status" options="Any,Open,Paid" value=AnyCheckbox "Only flagged orders"footerButton "Apply"
Label
Required. The sheet title.
Children
Any Elements
Position
Directly in Screen
Slots
footer
PropValuesDefaultDescription
desc"text"A description below the title.
openflagShows the Element open when the Preview loads. The export also renders it open first.
sideright | leftrightThe side that the Sheet comes from.

Export: The same as Dialog, with the Sheet* components and side.

Your components

Custom

A component of your app that Skizo does not have, for example a chat bubble. Use it only when no other Element type fits.

Custom "ChatBubble" author="Ana Souza" sent-at="9:41" mineText "Where is my order?"Custom "TypingIndicator"
Label
Required. The name of the component, in PascalCase. For example, ChatBubble.
Children
Any Elements
Position
Anywhere in a Screen
  • The name cannot be the name of an Element type. For a button, write Button.
  • A Custom takes all props and flags. skizo check does not check them.
  • The Preview shows a box with a dashed border. The box shows the name, the props and the children.

Export: import { ChatBubble } from "@/components/chat-bubble" and <ChatBubble author="Ana Souza" sentAt="9:41" mine>. A prop key with a hyphen becomes camelCase. A Custom without children becomes a self-closing tag. If the file imports a different component with the same name, the export renames your component: Label as CustomLabel.

Examples

These sketches come from the examples folder. For each sketch, the page shows the Skizo source next to its Preview. In the Preview, you can click these items:

  • Links and Buttons with to= show other Screens.
  • Buttons with opens= show Dialogs and Sheets.
  • Tabs, Menus and Selects open when you click them.

The other tabs above the source show the output of skizo export --target shadcn.

Each Preview opens at desktop width and is smaller to fit this page. To see the sketch at tablet or phone width, click Tablet or Phone in the toolbar of the Preview.

orders.skz

The Skizo source has 34 lines and 1,144 characters. The shadcn export has 3 files, 264 lines and 11,571 characters, 10.1 times as many.

SidebarLink "Dashboard" icon=home to=dashboardLink "Orders" icon=package to=orders Screen "Orders" id=orders subtitle="Manage and track customer orders"actionsButton "New order" icon=plus opens=new-orderGrid cols=3Card "Revenue" desc="Last 30 days"Stat "$12,450" hint="+12% vs last month" trend=upCard "Pending" desc="Awaiting payment"actionsMenu "Pending actions" icon=ellipsis icon-only variant=ghostItem "Send reminders"Item "Cancel all" destructiveBadge "18 orders" variant=secondaryCard "Avg. order value"Stat "$86.20"TabsTab "All" activeTable cols="Order,Customer,Status,Amount"| #1042 | Ana Souza | Paid | $120.00| #1041 | Bruno Lima | Open | $64.50Tab "Open"Table cols="Order,Customer,Amount" rows=5Dialog "New order" id=new-order desc="Create an order for a customer."Input "Customer name" placeholder="Jane Doe"Select "Status" options="Open,Paid" value=OpenfooterButton "Cancel" variant=outlineButton "Create order" Screen "Dashboard" id=dashboardText lorem=30

analytics.skz

The Skizo source has 27 lines and 1,140 characters. The shadcn export has 1 file, 168 lines and 7,502 characters, 6.6 times as many.

Screen "Analytics" id=analytics subtitle="Traffic and conversion for the last 30 days"actionsButton "Export" variant=outline icon=downloadGrid cols=4Stat "24,310" title="Visitors" hint="+8.2% vs last period" trend=upStat "3.4%" title="Conversion" hint="-0.3% vs last period" trend=downStat "$48,200" title="Revenue" hint="+12% vs last period" trend=upStat "1m 52s" title="Avg. session"Grid cols=3Card "Traffic" desc="Visits per day" span=2Chart "Daily visits" type=area cols="Date,Visits" rows=14Card "Top channels"Chart "Visits by channel" type=pie cols="Channel,Visits"| Organic search | 11650| Direct | 6560| Referral | 3640| Social | 2460Alert "Tracking paused on 2 pages" text="Add the snippet to /pricing and /blog to resume."GridCard "Revenue" desc="Per month, this year and last"Chart "Revenue per month" type=bar cols="Month,This year,Last year" rows=6Card "Goals"Stack gap=smText "Newsletter signups" size=smProgress value=64Text "Trial starts" size=smProgress value=38

chat.skz

The Skizo source has 45 lines and 1,719 characters. The shadcn export has 3 files, 206 lines and 9,278 characters, 5.4 times as many.

SidebarLink "Ana Souza" icon=user to=anaLink "Design team" icon=users to=design Screen "Ana Souza" id=ana subtitle="Active now"actionsButton "Conversation details" icon=info icon-only variant=ghost opens=detailsMenu "More" icon=ellipsis icon-only variant=ghostItem "Mute"Item "Archive"Item "Delete conversation" destructiveStack gap=mdCustom "ChatBubble" author="Ana Souza" sent-at="9:41"Text "Hey! Did you get a chance to look at the new pricing page?"Custom "ChatBubble" author="You" sent-at="9:43" mineText "Yes, just now. The comparison table reads much better."Custom "ChatBubble" author="Ana Souza" sent-at="9:44"Text "Great. Can we ship it on Friday?"SeparatorTextarea "Message" placeholder="Write a message..." rows=2Row justify=betweenButton "Attach file" icon=paperclip icon-only variant=outlineButton "Send" icon=sendSheet "Conversation details" id=details desc="Shared with Ana Souza since March."Row gap=sm align=centerAvatar "Ana Souza"Stack gap=smText "Ana Souza" boldText "ana@example.com" muted size=smSeparatorSwitch "Mute notifications"Switch "Show read receipts" checkedfooterButton "Block contact" variant=destructive Screen "Design team" id=design subtitle="4 members"Stack gap=mdCustom "ChatBubble" author="Bruno Lima" sent-at="10:02"Text "New icons are in the shared folder."Custom "ChatBubble" author="Grace Kim" sent-at="10:05"Text "Looks good. Badge colors need one more pass."SeparatorTextarea "Message" placeholder="Message Design team" rows=2Row justify=endButton "Send" icon=send

settings.skz

The Skizo source has 30 lines and 1,395 characters. The shadcn export has 1 file, 146 lines and 7,115 characters, 5.1 times as many.

Screen "Settings" id=settings subtitle="Manage your account and preferences"TabsTab "Profile"Card "Profile" desc="This is how others see you."Row gap=md align=centerAvatar "Ana Souza" size=lgStack gap=smText "Ana Souza" boldText "ana@example.com" muted size=smInput "Display name" value="Ana Souza"Input "Email" type=email value="ana@example.com"Textarea "Bio" placeholder="Tell us a little about yourself" rows=3footerButton "Save changes"Tab "Notifications"Card "Notifications" desc="Choose what we email you about."Switch "Product updates" checkedSwitch "Weekly digest"Switch "Security alerts" checked disabledSeparatorRadio "Email frequency" options="Instantly,Daily,Weekly" value=DailyTab "Danger zone"Alert "Deleting your account is permanent" text="All projects and data are removed. This cannot be undone." variant=destructiveButton "Delete account" variant=destructive icon=trash-2 opens=confirm-deleteDialog "Delete account?" id=confirm-delete desc="Type your email to confirm."Input "Email" type=email placeholder="ana@example.com"Checkbox "I understand this cannot be undone"footerButton "Cancel" variant=outlineButton "Delete account" variant=destructive

team.skz

The Skizo source has 39 lines and 1,576 characters. The shadcn export has 3 files, 275 lines and 11,957 characters, 7.6 times as many.

SidebarLink "Overview" icon=layout-dashboard to=overviewLink "Team" icon=users to=team Screen "Team" id=team subtitle="Invite people and manage their access"actionsButton "Invite member" icon=user-plus opens=inviteRow gap=smInput "Search" type=search placeholder="Search by name or email"Select "Role" options="All roles,Admin,Member,Viewer" value="All roles"Card "Members" desc="Showing 5 of 12 people."actionsMenu "Bulk actions" icon=ellipsis icon-only variant=ghostItem "Export as CSV"Item "Resend invites"Item "Remove selected" destructiveTable cols="Name,Email,Role,Status"| Ana Souza | ana@example.com | Admin | Active| Bruno Lima | bruno@example.com | Member | Active| Grace Kim | grace@example.com | Member | Invited| Diego Rocha | diego@example.com | Viewer | Active| Elena Costa | elena@example.com | Viewer | SuspendedSheet "Invite a member" id=invite desc="They get an email with a link to join."Input "Email" type=email placeholder="name@company.com"Select "Role" options="Admin,Member,Viewer" value=MemberTextarea "Message" placeholder="Optional note"footerButton "Send invite" icon=send Screen "Overview" id=overviewGrid cols=3Card "Members"Stat "12" hint="+2 this month" trend=upCard "Pending invites"Stat "3" hint="Oldest sent 4 days ago"Card "Seats used"Progress value=75Text "12 of 16 seats" muted size=smButton "Manage team" variant=outline icon=arrow-right to=team