dio.la

Dani Guardiola’s blog

Jan 1, 1970January 1st, 1970 · 2 minute read · tweet

Compound components

Draft.

Namespaces

Flat

tsx
<Menu> <MenuButton /> <MenuPopover> <MenuItem /> <MenuItem /> </MenuPopover></Menu>

Dot

tsx
<Menu.Root> <Menu.Button /> <Menu.Popover> <Menu.Item /> <Menu.Item /> </Menu.Popover></Menu.Root>

Overloaded

tsx
<Menu> <Menu.Button /> <Menu.Popover> <Menu.Item /> <Menu.Item /> </Menu.Popover></Menu>

Exports

Named

tsx
export function Menu() {}export function MenuButton() {}// ...

Object

tsx
export const Menu = { Root: function MenuRoot() {}, Button: function MenuButton() {}, // ...};

Note: export default {} is also possible, but not ideal.

Overloaded

tsx
export function Menu() {}Menu.Button = function MenuButton() {};// ...

Note: export default function ... is also possible, but not ideal.

Imports

Named

tsx
import { Menu, MenuButton } from "ui-package";<Menu> <MenuButton /> {/* ... */}</Menu>;

Local "as"

tsx
import * as Menu from "ui-package/menu";<Menu.Root> <Menu.Button /> {/* ... */}</Menu.Root>;

Global "as"

tsx
import * as UI from "ui-package";<UI.Menu> <UI.MenuButton /> {/* ... */}</UI.Menu>;

Options

Flat + named export

tsx
// Menu.tsxexport function Menu() {}export function MenuButton() {}

Pros and cons:

ScoreDescriptionNotes
✅Tree-shakableIn all import styles, most bundler tools will tree-shake unused component parts.
✅Auto-importsIDEs will automatically import the components (or parts) you use.
❌Component autocompleteAt most, you can start typing the name of the component (e.g. "Menu") to filter out other suggestions.
🟠Library autocompleteThe library namespace contains all parts of all components, making it hard to see available components at a glance.

Supports:

tsx
// named importimport { Menu, MenuButton } from "ui-package"; // or "ui-package/menu"<Menu> <MenuButton /></Menu>;
tsx
// global "as" importimport * as UI from "ui-package";<UI.Menu> <UI.MenuButton /></UI.Menu>;

Also this (but ew):

tsx
// local "as" importimport * as Menu from "ui-package/menu";<Menu.Menu> <Menu.MenuButton /></Menu.Menu>;

Dot + named export

ScoreDescriptionNotes
✅Tree-shakableThis is the only option for dot namespacing that is tree-shakable.
❌Auto-importsUsers need to manually write the local "as" import every time.
✅Component autocompleteSuggestions for all parts (and only the parts) will show up after the dot.
❌Library autocompleteIt's not possible to re-export at the top level since that'd cause conflicts (e.g. multiple Root parts of different components).
tsx
// Menu.tsxexport function Root() {}export function Button() {}

Supports:

tsx
// local "as" importimport * as Menu from "ui-package/menu";<Menu.Root> <Menu.Button /></Menu.Root>;

Dot + object export

tsx
// Menu.tsxexport const Menu = { Root: function MenuRoot() {}, Button: function MenuButton() {},};
ScoreDescriptionNotes
❌Tree-shakableSince it's exported as a single object, it's not tree-shakable.
✅Auto-importsIDEs will automatically import the components you use.
✅Component autocompleteSuggestions for all parts (and only the parts) will show up after the dot.
✅Library autocompleteOnly the components (objects) will show up in the library autocomplete (and not their parts).

Supports:

tsx
// named importimport { Menu } from "ui-package"; // or "ui-package/menu"<Menu.Root> <Menu.Button /></Menu.Root>;
tsx
// global "as" importimport * as UI from "ui-package";<UI.Menu.Root> <UI.Menu.Button /></UI.Menu.Root>;

Also this (but ew):

tsx
// local "as" importimport * as Menu from "ui-package/menu";<Menu.Menu.Root> <Menu.Menu.Button /></Menu.Menu.Root>;

Overloaded

tsx
// Menu.tsxexport function Menu() {}Menu.Button = function MenuButton() {};
ScoreDescriptionNotes
❌Tree-shakableSince it's exported as a single function with properties, it's not tree-shakable.
✅Auto-importsIDEs will automatically import the components you use.
🟠Component autocompleteSuggestions for all parts will show up after the dot. Unfortunately, so will the function prototype methods and properties (apply, call, etc).
✅Library autocompleteOnly the root components will show up in the library autocomplete (and not their parts).

Supports:

tsx
// named importimport { Menu } from "ui-package"; // or "ui-package/menu"<Menu> <Menu.Button /></Menu>;
tsx
// global "as" importimport * as UI from "ui-package";<UI.Menu> <UI.Menu.Button /></UI.Menu>;

Also this (but ew):

tsx
// local "as" importimport * as Menu from "ui-package/menu";<Menu.Menu> <Menu.Menu.Button /></Menu.Menu>;

Keep up with my stuff. Zero spam.

You're looking at my drafts!

Visit the main site: dio.la