UI Folder Overview

ui/ holds components that draw a piece of screen and are not bound to one model. Pages and module components import them by name instead of repeating the markup.
App UI
Components one app owns, such as an admin header, a landing hero, a dashboard widget or an app-only interaction. Keep the folder shallow.
@apps/myapp/ui
Library UI
Components several apps share, such as auth gates, responsive wrappers, editor pieces or common form fields.
@libs/shared/ui
Words used on this page
barrel
A folder's index.ts that re-exports its files, so callers import one path: @apps/myapp/ui.
"use client"
The first line that makes a file a client component. Without it, a component renders on the server.
namespace component
Several components exported as one object, used as Only.Web or Chart.Bar.
sidecar
A camelCase helper or type file that serves one component, like swipeCard.util.ts.
Does it belong in ui/?
Ask two questions: does it draw JSX or define a look, and does it take one model? Yes, then no, means ui/.
Code
ui/
lib/<model>/
webkit/
Draws JSX or defines a look, bound to no model — ui/
landing hero · admin header
✓
Belongs to one app, so it lives in apps/<app>/ui.
Only.Admin · Only.Web
✓
An auth gate or responsive wrapper several apps share, so it lives in libs/<lib>/ui.
Chart · MapView · Editor
✓
Wraps a third-party package that pages and module files may not import directly.
cardRecipe · panelRecipe
✓
A look several screens share. Not a component or a hook, but it lives in ui/Recipe/.
Goes somewhere else
a card for one order
✓
It is bound to a model, so it is Order.Unit.tsx in lib/order/.
useGeoLocation
✓
A hook or browser helper with no markup of its own.
✓Goes hereNot here

Import From The Barrel

Everything in ui/ is imported through the folder's barrel, never through a file path. A page uses AutoClose like this:
apps/myapp/page/signin/done.tsx
What each kind of file in ui/ turns into at the import site:
File in ui/How to use it
ui/AutoClose.tsximport { AutoClose } from "@apps/myapp/ui"
ui/Only/index.tsximport { Only } from "@libs/shared/ui", then <Only.Web>.
ui/Recipe/panel.tsimport { panelRecipe } from "@apps/myapp/ui", through Recipe/index.ts.
ui/swipeCard.util.tsNot in the barrel. SwipeCard.tsx imports it as ./swipeCard.util.
  • Top-level PascalCase names only. A file or folder directly under ui/ with a PascalCase name is exported; a camelCase sidecar stays private.
  • Never edit ui/index.ts. Add or rename the component file; akan start updates the barrel on save, and akan sync does it otherwise.
  • No deep paths. @apps/myapp/ui/AutoClose goes past the barrel, and lint rejects it in pages and module files.

Composite Components

Some components read better as one grouped name, such as Only.Admin or Only.Web. Give them a folder whose index.tsx exports that one name.
Each member is an ordinary component file. Web reads the store, so it is a client file:
libs/shared/ui/Only/Web.tsx
The folder's index.tsx gathers the members into one object, with no "use client":
libs/shared/ui/Only/index.tsx
A page imports the one name and picks a member:
apps/myapp/page/_index.tsx
  • { agent: false } keeps the width private. The component still subscribes to innerWidth, but the in-page agent cannot read it.
  • This index.tsx is yours. Unlike the generated ui/index.ts, a folder's namespace file is ordinary source you write and edit.
Heavy components: the index_.tsx pair
A chart, map or editor pulls in a large package that often touches window on import. Split the folder's entry into two files. First, index_.tsx loads the members with lazy():
libs/util/ui/Chart/index_.tsx
Then index.tsx, with no directive, builds the namespace the rest of the app imports:
libs/util/ui/Chart/index.tsx
  • ssr: false for browser-only packages. The server renders the loading fallback, and the real chart mounts in the browser.
  • lazy() targets use export default. ./Bar exports its component as the module default.
  • Keep the two files apart. Merging them puts the namespace in a client file, which breaks for the reason in the warning above.

Practical Rules

  • One file, one export, one name. AutoClose.tsx exports AutoClose.
  • Keep app ui/ shallow. Add a folder only when the components are naturally a grouped API such as Only.Web or Only.Admin.
  • Import from the barrel. Use @apps/myapp/ui or @libs/shared/ui, never a path into a file.
  • Server first. Add "use client" only for a hook, an event handler, the store, a browser global or a client-only package.
Common mistakes
MistakeFix
"use client" on a component that only renders markupDelete it unless the file uses a hook, handler, the store, a browser global or client-only package.
A card that takes one Order in ui/Move it to lib/order/Order.Unit.tsx.
Importing a component by its file pathUse @apps/myapp/ui, not @apps/myapp/ui/AutoClose. The deep path fails lint.
Adding a line to ui/index.ts by handLeave it. Add, rename or delete the component file instead.
Building Only = { … } in a "use client" fileBuild the object in a directive-free index.tsx.
An async component in ui/Await in the page and pass the result down as a prop.
Importing a third-party package in a page or *.Unit.tsxWrap it in a lib ui/ component and import that.
The same card classes copied into many filesAdd one recipe in ui/Recipe/ and call it everywhere.
Related pages

Released under the MIT License

Connect your AI to these docs

MCPhttps://akanjs.com/mcp
Copyright © 2026 Akan.js All rights reserved.System managed bybassman