Getting Started
CDN usage
Full bundle (simplest, registers all components): cdn.js is a pre-bundled IIFE file; just load it via a <script> tag:
<link rel="stylesheet" href="https://unpkg.com/@oas-ui/theme@2/index.css" />
<script src="https://unpkg.com/@oas-ui/ui@2/dist/cdn.js"></script>
<oas-button type="primary">Hello OAS-UI</oas-button>Per-family bundles (a middle ground between size and coverage): components are grouped into 9 feature families. Each family ships as its own IIFE file and inlines the base (runtime plus config-provider/app/theme-editor), so no extra script is required:
| Bundle | Components covered |
|---|---|
cdn/basic.js | Basic: button, icon, tag, badge, space, divider, link, typography, button-group, label, kbd, visually-hidden |
cdn/layout.js | Layout: layout, sidebar, container, grid, flex, splitter, scroll-area, masonry, aspect-ratio |
cdn/form.js | Form: input, textarea, checkbox, radio, switch, segmented, slider, input-number, rate, select, auto-complete, combobox, cascader, tree-select, mentions, date-picker, time-picker, calendar, upload, transfer, color-picker, toggle-button, toggle-group, pin-input, dynamic-input, dynamic-tags, editable, form, etc. |
cdn/feedback.js | Feedback & Overlays: tooltip, popover, hover-card, message, notification, toast, snackbar, backdrop, modal, confirm, drawer, popconfirm, alert, progress, loading-bar, spin, skeleton, empty, result, bottom-sheet, notice-bar, pull-refresh |
cdn/navigation.js | Navigation: menu, dropdown, contextmenu, command, menubar, navigation-menu, toolbar, breadcrumb, anchor, back-top, tour, tabs, bottom-navigation, pagination, steps, stepper, affix, page-header, float-button, speed-dial, app-bar, index-bar |
cdn/data.js | Data display: table, tree, virtual-list, card, avatar, avatar-group, image, qrcode, watermark, collapse, descriptions, timeline, list, carousel, statistic, countdown, ellipsis, chart, code, equation, log, marquee, number-animation, gradient-text, comment, etc. |
cdn/conversation.js | Conversation: bubble, attachment, message-scroller, marker, message-row |
cdn/workbench.js | Workbench: titlebar, statusbar, inspector, action-bar |
cdn/framework.js | Framework containers: config-provider, app, theme-editor |
Basic family only:
<link rel="stylesheet" href="https://unpkg.com/@oas-ui/theme@2/index.css" />
<script src="https://unpkg.com/@oas-ui/ui@2/dist/cdn/basic.js"></script>
<oas-button type="primary">Hello OAS-UI</oas-button>Add a feedback family (a page that combines forms and message toasts):
<link rel="stylesheet" href="https://unpkg.com/@oas-ui/theme@2/index.css" />
<script src="https://unpkg.com/@oas-ui/ui@2/dist/cdn/basic.js"></script>
<script src="https://unpkg.com/@oas-ui/ui@2/dist/cdn/form.js"></script>
<script src="https://unpkg.com/@oas-ui/ui@2/dist/cdn/feedback.js"></script>Choosing a rule: load per-family bundles when the components you need span ≤ 2 families (smaller than the full bundle); when they span ≥ 3 families, load the single full
cdn.jsinstead — every family inlines the base, so stacking multiple families costs more duplicated base code than the all-in-one file.
Import a single component on demand: use the esm.sh short path (resolves dependencies automatically), which registers only that one component:
<script type="module">
import 'https://esm.sh/@oas-ui/ui@2/basic/button'
</script>
<oas-button type="primary">Hello OAS-UI</oas-button>For multiple components, add one short path each — only the components you use and their dependency chains are downloaded (e.g. the button chain is ≈ 27KB gzip, including the core runtime; on-demand mode does not include the full icon set — using built-in icon names needs import '@oas-ui/icons/register', see above); unused components cost nothing:
<script type="module">
import 'https://esm.sh/@oas-ui/ui@2/basic/button'
import 'https://esm.sh/@oas-ui/ui@2/basic/tag'
</script>CDN entry-point note: the
oas-*.jsfiles under each component directory are pure class definitions (for bundler tree-shaking) and do not register anything;customElements.defineruns inindex.js. For direct CDN usage, use the fullcdn.jsabove or the on-demand short path (resolved toindex.jsvia the exports map). Do not importdist/basic/button/oas-button.jsdirectly — the import succeeds but the element never registers, so nothing renders.
Install (npm / pnpm / yarn)
pnpm add @oas-ui/ui @oas-ui/themeImport
Full import (auto-registers all components):
import '@oas-ui/theme'
import '@oas-ui/ui'Import a single component on demand:
import '@oas-ui/theme'
import '@oas-ui/ui/basic/button'On-demand mode does not include the full built-in icon set (so each component chain doesn't carry every icon). If you use a built-in icon name (e.g.
<oas-button icon="check">,<oas-icon name="close">), addimport '@oas-ui/icons/register'once (the full entry@oas-ui/uialready includes it). Not needed if you only supply icons viaregisterIcon/registerIconLibrary/ iconfont / slot.
Use directly in React / Vue (rendering, attributes and slot content work in both; event listening — see the section below):
// React: event listening — see "React event listening" below (native onoas-* lowercase works)
<oas-button type="primary" ref={btnRef}>Button</oas-button><!-- Vue: @oas-click listens to the custom event directly -->
<oas-button type="primary" @oas-click="onClick">Button</oas-button>Rendering and attribute passing need no wrappers in any of the three environments (React / Vue / vanilla). Events are dispatched as oas-* CustomEvents — Vue can listen via @oas-click directly; React 19 works with the native onoas-* lowercase form, while the camelCase convention needs the optional bridge (below).
React event listening
React 19 listens natively: only the "on + lowercase literal" form works — <oas-button onoas-submit={...}> does catch oas-submit; the idiomatic camelCase form <oas-button onOasSubmit={...}> silently fails (the prop is never mapped to an oas-submit listener on custom elements), lowercase dashed prop names are hostile to TS/JSX typing, and React 17/18 is not supported either.
// React 19: native form, no extra dependency
<oas-button type="primary" onoas-submit={(e) => console.log(e.detail.value)}>
Submit
</oas-button>Optional utility @oas-ui/react (in-repo, not released separately): when you need camelCase convention, type safety, or React 17/18 support, use the hooks — source lives in packages/react (reference it inside the monorepo); it is not published to npm.
import { useRef } from 'react'
import { useOasEvents } from '@oas-ui/react'
function Demo() {
const btnRef = useRef<HTMLElement | null>(null)
useOasEvents(btnRef, {
'oas-submit': (detail) => console.log('submit', detail.value),
'oas-cancel': () => console.log('cancel'),
})
return (
<oas-button type="primary" ref={btnRef}>
Submit
</oas-button>
)
}For a single event use useOasEvent<Detail>(ref, 'oas-submit', handler). Both hooks always run the latest closure after re-renders and unbind automatically on unmount; event names must carry the oas- prefix. Full details live in packages/react/README.md.
Capability sub-packages (heavy capabilities ship with the main entry): a few components split heavy optional capabilities into standalone sub-packages that are bundled into the component's main entry — importing @oas-ui/ui/<group>/<component> from any main path includes every capability by default (import registers it; no extra import needed):
| Component | Capability (shipped with the main entry) | Capability subpath (for /core) |
|---|---|---|
oas-table | Inline editing (editable / editor / actions) | @oas-ui/ui/data/table/edit |
oas-tabs | Double-click rename / context menu / drag-sort (editable / context-menu / sortable) | @oas-ui/ui/navigation/tabs/manager |
oas-modal | Prompt dialog (modal.prompt()) | @oas-ui/ui/feedback/modal/prompt |
oas-popover | Cursor-anchored context menu / touch long-press / placement·size breakpoint shorthands | @oas-ui/ui/feedback/popover/contextmenu |
oas-color-picker | 2D color field / gradient designer (mode="gradient") | @oas-ui/ui/form/color-picker/designer |
import '@oas-ui/ui/data/table' // the main entry already includes inline editingIf you prefer a lean core, import the new /core path of these components instead (e.g. @oas-ui/ui/data/table/core) — it omits the capabilities above, so the related options silently no-op with a dev-mode warning once; add the capability sub-path explicitly (import registers it) or switch back to the main entry when you need them:
import '@oas-ui/ui/data/table/core' // pure core: no inline editing
import '@oas-ui/ui/data/table/edit' // explicit capability import (core path only)Import order does not matter: capabilities registered later are retroactively applied to already-mounted elements. The full entry @oas-ui/ui and the CDN family bundles already include every capability — no extra imports needed there.
Want to see it live? The repo ships a Playground for React / Vue — run
pnpm dev:react/pnpm dev:vueinsidepackages/playground(orpnpm --filter @oas-ui/playground dev:reactfrom the repo root).
Browser support
Modern evergreen browsers: latest two major versions of Chrome / Edge / Firefox / Safari. Specifically:
- Chrome / Edge (Chromium): full e2e coverage
- Firefox: verified by real-browser testing (Firefox-specific pseudo-elements such as the slider track are adapted)
- Safari ≥ 16.4: verified on the WebKit engine; Declarative Shadow DOM requires 16.4+, solid-state coloring (
color-mix) requires 16.2+
Theme switching
<html data-theme="dark">
<!-- switches to dark theme -->
</html>Three built-in themes: light / dark / high-contrast. See the Theming guide for customization.
Your own CSS does not follow the theme automatically: variables inside components switch with
data-theme, but styles you write yourself must referencevar(--oas-color-*)to follow along. The most common pitfall is a page body without an explicit background — after switching to dark, components turn dark while the page stays at the browser default white:
cssbody { background: var(--oas-color-bg); color: var(--oas-color-text-primary); }
Event conventions (important)
All component events carry the oas- prefix: oas-change, oas-select, oas-input, oas-close, etc. (each component's API table lists the full set and when they fire). This is deliberate — Web Components events bubble to window by default, and unprefixed change/select would collide with native events and host-framework synthetic events; the prefix makes the origin unambiguous.
// correct: oas- prefix
menu.addEventListener('oas-select', (e) => console.log(e.detail))
// wrong: never fires (a common first pitfall)
menu.addEventListener('select', (e) => console.log(e.detail))Controlled components (switch / radio-group / checkbox-group / slider / input-number, etc.) write the latest value back to the host attribute after user interaction (e.g. value / checked), so el.getAttribute('value') reads the current state directly, consistent with the oas-change event's detail.value.
SSR
For server-side rendering, refer to the SSR strategy: side-effect imports of the component library should only run on the client.
For more integration questions (the ::part() customization trap, event timing, etc.), see the FAQ.