Back to Puter

Puter Web Components

src/puter-js/src/ui/components.md

26.07.212.3 KB
Original Source

Puter Web Components

A suite of web components bundled with the Puter SDK (puter.js) so UI works in any environment — apps running standalone, as PWAs, on their own domain, or inside puter.com.

How they work

Load puter.js, and the components auto-register. You can then either:

  1. Use the imperative APIputer.ui.alert(...), puter.ui.contextMenu(...) etc. The SDK renders the right component automatically.
  2. Use the components directly in HTML or JS:
html
<puter-notification title="Hello" text="This works without puter.com"></puter-notification>
js
const alert = document.createElement('puter-alert');
alert.setAttribute('message', 'Hello');
alert.addEventListener('response', e => console.log(e.detail));
document.body.appendChild(alert);
alert.open();

All components use Shadow DOM so their styles won't leak into your page. Theming is via CSS custom properties (see Theming below).


Component Reference

TagPurposeMaps to SDK method
<puter-alert>Modal alert dialogputer.ui.alert()
<puter-prompt>Modal text input dialogputer.ui.prompt()
<puter-notification>Toast notificationputer.ui.notify()
<puter-context-menu>Context menu with submenusputer.ui.contextMenu()
<puter-menubar>Application menubarputer.ui.setMenubar()
<puter-spinner>Loading overlayputer.ui.showSpinner() / hideSpinner()
<puter-color-picker>Color picker dialogputer.ui.showColorPicker()
<puter-font-picker>Font picker dialogputer.ui.showFontPicker()

<puter-alert>

Modal alert with customizable buttons and optional type icon. Uses native <dialog> element (focus trap, Escape to close, backdrop).

Attributes

AttributeTypeDescription
messagestringThe alert message
typeerror | warning | info | success | confirmShows a matching icon with colored background

Properties

PropertyTypeDescription
buttonsArray[{ label, value?, type? }]. type can be primary, danger, success, warning, info, default. Last button is primary by default
optionsObjectPassthrough options object

Events

EventDetailWhen
responseButton valueUser clicked a button or dismissed

Example

js
const el = document.createElement('puter-alert');
el.setAttribute('message', 'Delete this file?');
el.setAttribute('type', 'warning');
el.buttons = [
    { label: 'Cancel', value: false, type: 'default' },
    { label: 'Delete', value: true, type: 'danger' },
];
el.addEventListener('response', e => console.log('User chose:', e.detail));
document.body.appendChild(el);
el.open();

<puter-prompt>

Modal dialog with a text input. Enter submits, Escape cancels.

Attributes

AttributeTypeDescription
messagestringThe prompt message shown above the input
placeholderstringPlaceholder for the input
default-valuestringPrefilled input value

Properties

PropertyTypeDescription
optionsObjectPassthrough options object

Events

EventDetailWhen
responsestring | falseThe entered value, or false if cancelled

Example

js
const el = document.createElement('puter-prompt');
el.setAttribute('message', 'Rename file');
el.setAttribute('default-value', 'untitled.txt');
el.addEventListener('response', e => {
    if (e.detail !== false) console.log('New name:', e.detail);
});
document.body.appendChild(el);
el.open();

<puter-notification>

Toast notification in the top-right corner with frosted-glass background. Auto-dismisses after duration ms. Multiple notifications stack vertically.

Attributes

AttributeTypeDescription
titlestringNotification title
textstringNotification body text (up to 2 lines)
iconstringIcon URL (if omitted, a default icon matching type is shown)
round-icon(boolean)Presence makes the icon image circular
typeinfo | success | warning | errorChooses default icon and accent color
durationnumberAuto-dismiss delay in ms (default 5000, 0 = never auto-dismiss)

Events

EventDetailWhen
click{}User clicked the notification body
close{}Notification dismissed (by user or timer)

Example

js
const n = document.createElement('puter-notification');
n.setAttribute('title', 'Saved');
n.setAttribute('text', 'Your changes have been saved successfully.');
n.setAttribute('type', 'success');
n.setAttribute('duration', '3000');
document.body.appendChild(n);

<puter-context-menu>

Positioned menu with nested submenus, icons, keyboard shortcuts, and optional danger-styled items. On mobile (≤480px or coarse pointer), automatically switches to a bottom action sheet with backdrop.

Attributes

AttributeTypeDescription
xnumberLeft position in pixels (ignored in sheet mode)
ynumberTop position in pixels (ignored in sheet mode)
data-submenu(boolean)Marks this as a nested submenu (skips sheet mode)

Properties

PropertyTypeDescription
itemsArrayMenu items (see Item Schema below)

Item schema

FieldTypeDescription
labelstringItem text
iconstringSVG string (starts with <) or image URL
actionFunctionCalled when item is selected
itemsArrayNested submenu items
disabledbooleanGreyed out, not selectable
typedangerRenders item in red
dangerbooleanAlias for type: 'danger'
shortcutstringRight-aligned keyboard shortcut hint (e.g., '⌘C')
checkedbooleanShows a checkmark column (reserves space when undefined on other items)
separatorbooleanRenders as a divider (or use string '-' as the item)

Events

EventDetailWhen
selectSelected itemUser chose a menu item
close{}Menu was dismissed

Example

js
const menu = document.createElement('puter-context-menu');
menu.setAttribute('x', '100');
menu.setAttribute('y', '200');
menu.items = [
    { label: 'Copy', icon: copyIcon, shortcut: '⌘C', action: () => doCopy() },
    { label: 'Paste', icon: pasteIcon, shortcut: '⌘V', action: () => doPaste() },
    '-',
    { label: 'More', items: [
        { label: 'Option A' },
        { label: 'Option B' },
    ]},
    '-',
    { label: 'Delete', icon: trashIcon, type: 'danger', action: () => doDelete() },
];
document.body.appendChild(menu);

<puter-menubar>

Fixed top-of-page application menubar. Clicking an item opens its dropdown (rendered via <puter-context-menu>). Hover switches dropdowns while one is open.

Properties

PropertyTypeDescription
itemsArrayTop-level items (uses same schema as context menu)

Events

EventDetailWhen
selectSelected itemUser chose a menu item from any dropdown

Example

js
const menubar = document.createElement('puter-menubar');
menubar.items = [
    { label: 'File', items: [
        { label: 'New', shortcut: '⌘N', action: () => newDoc() },
        { label: 'Open…', shortcut: '⌘O', action: () => openDoc() },
        '-',
        { label: 'Save', shortcut: '⌘S', action: () => save() },
    ]},
    { label: 'Edit', items: [
        { label: 'Undo', shortcut: '⌘Z', action: () => undo() },
        { label: 'Redo', shortcut: '⇧⌘Z', action: () => redo() },
    ]},
];
document.body.appendChild(menubar);

<puter-spinner>

Full-page loading overlay with a spinner and optional message.

Attributes

AttributeTypeDescription
textstringOptional text shown below the spinner

Methods

MethodDescription
open()Show the overlay (called automatically on append)
close()Hide and remove

Example

js
const s = document.createElement('puter-spinner');
s.setAttribute('text', 'Loading…');
document.body.appendChild(s);
setTimeout(() => s.close(), 2000);

<puter-color-picker>

Modal color picker with 80 preset swatches, a hex input, and a native HTML5 color input for arbitrary colors.

Attributes

AttributeTypeDescription
default-colorstringInitial hex color (e.g., #3b82f6)

Events

EventDetailWhen
responsestring | nullSelected hex color, or null if cancelled

Example

js
const el = document.createElement('puter-color-picker');
el.setAttribute('default-color', '#ff6b35');
el.addEventListener('response', e => {
    if (e.detail) console.log('Picked:', e.detail);
});
document.body.appendChild(el);
el.open();

<puter-font-picker>

Modal font picker with curated web-safe fonts grouped by category (System, Sans Serif, Serif, Monospace, Cursive). Has search and live preview. Double-click a font to select and close.

Attributes

AttributeTypeDescription
default-fontstringInitial font name (e.g., 'Georgia')

Events

EventDetailWhen
response{ fontFamily: string } | nullSelected font (full font-family stack), or null if cancelled

Example

js
const el = document.createElement('puter-font-picker');
el.setAttribute('default-font', 'Georgia');
el.addEventListener('response', e => {
    if (e.detail) document.body.style.fontFamily = e.detail.fontFamily;
});
document.body.appendChild(el);
el.open();

Styling

All components render inside Shadow DOM and match puter.com's native GUI appearance. Styles are encapsulated and cannot be overridden from outside the component.

Dark mode

By default, components that ship dark-mode styles auto-adapt to the OS setting (prefers-color-scheme: dark): <puter-notification>, <puter-context-menu>, <puter-menubar>, <puter-color-picker>, <puter-font-picker>.

To force a specific theme regardless of the OS preference, set the theme attribute on the component:

ValueBehavior
darkAlways render in dark theme
lightAlways render in light theme
unsetFollow the system prefers-color-scheme (default)
html
<!-- Force dark, even in an OS configured for light mode -->
<puter-menubar theme="dark"></puter-menubar>

<!-- Force light, even when the OS is in dark mode -->
<puter-notification theme="light" title="Hello"></puter-notification>

The attribute is live — changing it at runtime re-paints the component immediately. For <puter-menubar> and <puter-context-menu>, the theme is forwarded to any dropdowns and submenus they spawn, so the whole tree stays in sync.

When you use the imperative API instead of the elements directly, pass a theme in the options and it is applied as the theme attribute on the rendered component:

js
puter.ui.setMenubar({ theme: 'dark', items: [ /* … */ ] });
puter.ui.contextMenu({ theme: 'dark', items: [ /* … */ ] });

This only takes effect when running standalone (puter.env === 'web'). Inside the Puter desktop (puter.env === 'app') the spec is handled by the desktop and the theme option is ignored.

Responsive / mobile

All components have mobile breakpoints at @media (max-width: 480px). Notable behaviors:

  • Context menu switches to a bottom action sheet with backdrop on mobile or coarse-pointer devices
  • Dialogs become full-width (minus 32px margin) with larger touch targets
  • Input fonts are 16px on mobile to prevent iOS zoom on focus
  • Notifications stretch edge-to-edge on narrow screens