Guides

Public API

Atom UI exposes three public API layers.

Namespace Exports

Namespace exports are the stable API for application and styled-layer usage.

import { Select } from "@flowstack-ui/atom";

<Select.Root>
  <Select.Trigger />
  <Select.Content />
</Select.Root>;

Subpath Exports

Subpaths are stable focused entrypoints.

import { Select } from "@flowstack-ui/atom/select";
import { Link } from "@flowstack-ui/atom/link";
import { Clipboard } from "@flowstack-ui/atom/clipboard";
import { Image } from "@flowstack-ui/atom/image";
import { useControllableState } from "@flowstack-ui/atom/hooks";

Every subpath must be listed in package.json and emit JavaScript plus declaration files.

Direct Part Exports

Direct exports are available from component subpaths for migration and advanced composition.

import { SelectRoot, SelectTrigger } from "@flowstack-ui/atom/select";

Prefer namespace usage for new code unless a direct part export improves local readability. Shared primitives retain their shared direct names. For example, Dialog.Root is directly exported as ModalRoot because Dialog composes the shared Modal root behavior. Check the component subpath declarations rather than assuming every direct export is named by concatenating its namespace and part names.

Non-API Files

The following are not public package API:

  • files under src/primitives/**
  • files under src/utils/**, except Portal through @flowstack-ui/atom/portal
  • tests
  • internal helper functions that are not exported from a public subpath

Changing non-API files can still be a behavior change if public output changes.

Composition

Components that document asChild clone their only child and merge Atom props onto it. Components that document render can replace the default element with an intrinsic tag, element, or render callback. Both paths preserve forwarded refs, native props, and Atom-owned behavior.

Choose a composed element whose native semantics match the interaction. For example, a Button link composition should expose its destination through an href prop so Atom preserves link semantics instead of adding button behavior:

import { Button } from "@flowstack-ui/atom";

<Button.Root asChild>
  <a href="/settings">Settings</a>
</Button.Root>;

The same rule applies to custom link adapters and render elements: keep href visible on the element passed to Button. This lets Button identify link semantics and replace href, target, and rel with null while disabled or loading. Native anchors and permissive adapters render without those attributes.

A router component that requires href to remain a string is not safe for direct inactive composition. Use a render adapter that bypasses the router component and returns a destination-free anchor when aria-disabled is true. Atom does not ship router-specific bindings, inspect framework-specific navigation props, or retain a live destination on an inactive link.

The generic Link.Root is narrower than Button link mode. Its default render requires href, while render or asChild may supply a router adapter that owns the final destination. Link does not add action, loading, disabled, or router-provider behavior and remains server-safe.