dio.la

Dani Guardiola’s blog

Jan 1, 1970January 1st, 1970 · 1 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.tsx
export 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 import
import { Menu, MenuButton } from "ui-package"; // or "ui-package/menu"
<Menu>
<MenuButton />
</Menu>;
tsx
// global "as" import
import * as UI from "ui-package";
<UI.Menu>
<UI.MenuButton />
</UI.Menu>;

Also this (but ew):

tsx
// local "as" import
import * 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.tsx
export function Root() {}
export function Button() {}

Supports:

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

Dot + object export

tsx
// Menu.tsx
export 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 import
import { Menu } from "ui-package"; // or "ui-package/menu"
<Menu.Root>
<Menu.Button />
</Menu.Root>;
tsx
// global "as" import
import * as UI from "ui-package";
<UI.Menu.Root>
<UI.Menu.Button />
</UI.Menu.Root>;

Also this (but ew):

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

Overloaded

tsx
// Menu.tsx
export 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 import
import { Menu } from "ui-package"; // or "ui-package/menu"
<Menu>
<Menu.Button />
</Menu>;
tsx
// global "as" import
import * as UI from "ui-package";
<UI.Menu>
<UI.Menu.Button />
</UI.Menu>;

Also this (but ew):

tsx
// local "as" import
import * 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