Skip to content

Extension guides

Shared UI

Import host-provided components so graphical extensions match NativePi's density, interaction states, and active surface without bundling a second component system.

Import components

import {
  Badge,
  Button,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogTitle,
  DialogTrigger,
} from "@nativepi/extension-api/ui";

These components resolve only inside NativePi. Importing them in the Pi entry or rendering them in a standalone browser throws an explicit host error.

Component groups

GroupExports
ActionsButton, Badge
InputsInput, Textarea, Label, Switch, Separator
FieldsField, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel
DialogsDialog, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription
MenusMenu, MenuTrigger, MenuContent, MenuGroup, MenuLabel, MenuItem, MenuSeparator
SelectsSelect, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectLabel, SelectItem, SelectSeparator

Buttons and badges

Button variants are default, secondary, destructive, outline, ghost, and link. Sizes are default, xs, sm, lg, xl, and matching icon sizes. Badge supports the same variants.

Dialogs and menus

<Dialog>
  <DialogTrigger render={<Button variant="outline">Open details</Button>} />
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Query details</DialogTitle>
      <DialogDescription>Review the query before running it.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose render={<Button variant="ghost">Cancel</Button>} />
      <Button>Run query</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Triggers follow Base UI composition. Pass an element through render; do not use Radix's asChild pattern. Menu and select content accept side, alignment, and offset props for placement.

<Menu>
  <MenuTrigger render={<Button size="sm" variant="ghost">Actions</Button>} />
  <MenuContent align="end">
    <MenuLabel>Result actions</MenuLabel>
    <MenuItem onClick={() => void context.actions.copyText(result)}>Copy result</MenuItem>
    <MenuSeparator />
    <MenuItem variant="destructive" onClick={clearResult}>Clear result</MenuItem>
  </MenuContent>
</Menu>

Settings rows

  • SettingsActionRow: label, optional description, and custom child action
  • SettingsSwitchRow: boolean value with onChange
  • SettingsSelectRow: string value, options, and onChange
  • SettingsTextRow: text or multiline value committed through onCommit
  • SettingsSliderRow: bounded numeric value, step, formatter, and onChange
<SettingsSwitchRow
  label="Confirm destructive queries"
  description="Ask before running DELETE, DROP, or TRUNCATE."
  checked={settings.confirmDestructive}
  onChange={(checked) => void updateSettings({ confirmDestructive: checked })}
/>

Extension-specific styling

NativePi's Tailwind build runs before renderer source is compiled, so package-specific utility classes do not generate CSS. Use inline styles for small layouts and semantic variables for color:

  • var(--foreground)
  • var(--muted-foreground)
  • var(--border)
  • var(--destructive)
  • var(--warning)
  • var(--success)
<div
  style={{
    display: "grid",
    gap: 8,
    color: "var(--foreground)",
    borderBlockEnd: "1px solid var(--border)",
  }}
>
  {children}
</div>

Icons

Use Phosphor icons to match NativePi. Mark an icon inside a button with data-icon="inline-start" or data-icon="inline-end" so spacing follows the button component.

Type reference

Exact prop interfaces are listed in the API reference and exported from @nativepi/extension-api/ui for editor completion.