Getting Started
Install Morphos and build your first headless primitive component.
This guide gets you from zero to a working, accessible component in a few minutes. If you'd rather understand the ideas first, read the Introduction — otherwise, let's build something.
Install PraxisJS
Morphos is built on top of PraxisJS, so it needs to be installed first.
npm install @praxisjs/core @praxisjs/decorators @praxisjs/runtime @praxisjs/jsxpnpm add @praxisjs/core @praxisjs/decorators @praxisjs/runtime @praxisjs/jsxyarn add @praxisjs/core @praxisjs/decorators @praxisjs/runtime @praxisjs/jsxbun add @praxisjs/core @praxisjs/decorators @praxisjs/runtime @praxisjs/jsxInstall Morphos
@morphos/core is a required peer dependency of every component package — install it alongside
whichever categories you actually need.
npm install @morphos/corepnpm add @morphos/coreyarn add @morphos/corebun add @morphos/coreThen add the component packages for what you're building:
npm install @morphos/inputs @morphos/overlays @morphos/layout @morphos/feedbackpnpm add @morphos/inputs @morphos/overlays @morphos/layout @morphos/feedbackyarn add @morphos/inputs @morphos/overlays @morphos/layout @morphos/feedbackbun add @morphos/inputs @morphos/overlays @morphos/layout @morphos/feedbackYou don't have to install all four — each one is independent. A form-heavy page might only need
@morphos/inputs; a dashboard with modals might add @morphos/overlays on top.
Configure TypeScript
Morphos requires the same TypeScript settings as PraxisJS:
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": false,
"jsx": "react-jsx",
"jsxImportSource": "@praxisjs/jsx"
}
}Build a simple component
Every Morphos component is a plain PraxisJS class component — no special setup, no providers.
Here's a counter built with Button:
import { Component, State } from '@praxisjs/decorators'
import { StatefulComponent } from '@praxisjs/core'
import { Button } from '@morphos/inputs'
@Component()
class Counter extends StatefulComponent {
@State() count = 0
render() {
return (
<Button onClick={() => this.count++}>
Clicked {() => this.count} times
</Button>
)
}
}Build a compound component
Components with multiple moving parts — dialogs, accordions, menus — follow the same pattern
throughout Morphos: create the root instance once with @State(), then pass it down to each part
as a prop named after the component.
import { Component, State } from '@praxisjs/decorators'
import { StatefulComponent } from '@praxisjs/core'
import {
Dialog,
DialogTrigger,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
} from '@morphos/overlays'
@Component()
class ConfirmEmail extends StatefulComponent {
@State() dialog = new Dialog()
render() {
return (
<>
<DialogTrigger dialog={this.dialog}>Change email</DialogTrigger>
<DialogContent dialog={this.dialog} aria-labelledby="dialog-title">
<DialogTitle id="dialog-title">Change your email</DialogTitle>
<DialogDescription>
We'll send a confirmation link to your new address.
</DialogDescription>
<DialogClose dialog={this.dialog}>Cancel</DialogClose>
</DialogContent>
</>
)
}
}No context provider, no hooks — this.dialog already knows whether it's open, traps focus while
it is, locks page scroll, and closes on Escape or backdrop click, all out of the box. Every
compound component in the library (Accordion, RadioGroup, Select, Toast, and the rest)
follows this exact same shape, so once you've built one you've basically built them all.
Every component page's Usage section shows this pattern applied to that specific component — check the full component list once you're ready to explore further.
Style what you built
Morphos ships zero CSS. Every piece of state is exposed as a data-* attribute on the root
element, so plain CSS selectors are all you need:
/* Style the disabled state */
[data-disabled] {
opacity: 0.5;
cursor: not-allowed;
}
/* Style an open dialog */
[role="dialog"][data-open] {
display: block;
}
/* Style a selected tab */
[role="tab"][data-selected] {
border-bottom: 2px solid currentColor;
font-weight: 600;
}No class toggling required — the component sets and clears the attribute for you. See the full list of attributes in the Introduction.
Prefer not to write these from scratch? @morphos/styles ships an
optional, opt-in CSS recipe for every component — install it and add one class name.