Skip to content
cccskills
FreePublished on GitHubEnglish overview

compose-atoms

Use this guide to split a large product feature into capability-focused React units that different hosts can choose to mount. It helps keep data, local UI state, and actions with the capability that needs them, while hosts assemble only what they use.

OriginalShow the original description

Use for splitting heavy domains into capabilities (原子组件) across page, portal, share or micro-app hosts, sinking state (状态下沉) and replacing mode/readOnly flags. Simple component splits: react.

lobehub/lobehub83,504 stars(GitHub 83,504 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Planning a feature shared by a page and a portal
  • Keeping optional capabilities out of a lightweight host
  • Moving domain state into the component that uses it
  • Replacing read-only or mode flags with separate units

Overview

What it does

This guide shows how to divide a large feature into capability-focused units that pages, portals, shared screens, or micro-apps can combine as needed. The goal is to make each unit something a host can leave out, rather than simply splitting the visual layout into smaller files. Data fetching, display data, temporary UI state, and update actions belong with the unit that uses them. The assembler handles layout and chooses which units to load. This structure helps keep lightweight screens from importing dependencies for features they do not use.

When it helps

Use this approach when the same feature appears in several places, such as a full page and a public sharing page, and each place needs different editing or interaction capabilities. It can also help when new features keep adding mode or readOnly branches, or when you want to move state out of the screen assembler. For a simple component split within one screen, use react.

Example requests

  • “Organize this feature into capability units for a page and a public screen, and show how to assemble them.”
  • “Remove the read-only flag and load editing capabilities only on screens that need them.”
  • “Move data fetching and temporary state into the appropriate units.”

Notes

After splitting, inspect the import tree of the lightweight host and confirm that it does not load modules it does not need.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
AGoodThe described work is design and import inspection, without deletion or external sending.
Easy setupDoes it need few tools or API keys, and are they documented?
AGoodIt is a React design guide with no stated extra tools or credentials.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (1)

  • SKILL.md10.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Compose Atoms

A heavy domain is a kit, not a viewer with modes. Each host is an import list. An atom owns the data, actions, and dependencies of one capability. The assembler only chooses what to mount.

This is a module-graph split. Hiding UI does not drop a module. A host that never imports a file is the only host that does not ship it.

Do not use this skill to slice one component into smaller files. That is react.

When

The folder is a product surface (Conversation, Acceptance, Document, Verify, Task, …) and at least one of these is true:

  • One entry owns read + write + workflow + host integration.
  • A second host already exists or is the next change (page, portal, share, mobile, popup, micro-app).
  • The next feature is another boolean / mode / variant on the fat tree.
  • Reusing a header, list, or card would import trays, stores, or chat.

If the file is large but has one capability and one host, stop. Use react.

Grain

An atom is the smallest unit a host is allowed not to mount.

Ask, for every chunk: would any host ship the rest and skip this? If yes, it is an atom. If no, it stays inside its parent.

That grain is coarser than a visual section and finer than the whole page.

Too coarseRight grainToo fine
One Viewer with readOnlyIdentity, goal, list, decision, …Every badge, row, and icon as a public export
Chat as a single importList, composer, intervention, …Every message sub-row the page then rewires
Document as editor + every panelCanvas, header, comments as omittedInternal toolbar buttons

A workflow (focus review, thread, publish) is one atom to hosts that skip the whole mode. Inside it, keep splitting only if a host would mount a piece of that workflow alone.

Sink State

The split is worthless if the assembler still holds the domain state. Imports follow the hook. A useStore / useX / handleAccept left on the page keeps that module (and everything it imports) on every host that mounts the page.

Sink everything the atom needs into the atom:

Sinks into the atomStays on the assembler
Resource fetchThe record id / scope identity
Derived view model (counts, labels, scope)Which atoms are mounted (the import list)
Transient UI (filter, collapse, pending)Host layout state (focused vs overview, rail)
Mutation handlers and their stores/modalsHost seams (provider value the runtime owns)
// wrong — visual split, state still on the page
const Page = () => {
  const { data, mutate } = useX(id);
  const [filter, setFilter] = useState('all');
  const accept = () => void mutate(...);
  return (
    <>
      <Identity data={data} />
      <List data={data} filter={filter} onFilter={setFilter} />
      <DecisionBar onAccept={accept} />
    </>
  );
};

// right — each atom reads and acts; page only assembles
const Page = () => (
  <Scope id={id}>
    <Identity />
    <List />
    <Decision />
  </Scope>
);

Do not lift "to fetch once" or "so siblings share data". Two atoms calling the same SWR hook share the cache by key. Sibling writes go through that cache (or a store slice the write atom imports), not through page useState.

Lift only when the state is the assembler's job: which workflow is on screen, whether a rail is open. If a value is used to render or mutate one capability, it belongs in that capability.

Kit

PieceOwnsMust not own
Domain primitiveTypes, predicates, formattersReact, stores, services
Data accessOne resource, one fileSibling list / infinite / document hooks
Read atomPresent + local UI state + its own fetchMutations, trays, owner services, host stores
Write atomOne mutation and its UIPage layout, unrelated writes
Workflow atomA mode assembled from other atomsBecoming the only entry other hosts can import
Domain slotReactNode hole for another same-domain atomA callback whose implementation lives in this file
Host seamOptional host-provided UI, null by defaultA fallback that imports the host graph
AssemblerLayout + the import listDomain actions; domain fetch except local chrome

A visual block that both reads and writes is a read atom + a write atom, joined by a domain slot.

Two seam types — do not mix them:

  • Domain slot — same feature, optional capability (editSlot, toolbar). The host imports the write atom and passes it in.
  • Host seam — only some runtimes can resolve it (chat store, topic drawer, Electron). Context default is null. The micro-app / share page never imports the provider value. See split-micro-app for the runtime cut.

Ownership

State and imports live in the atom that needs them.

  • Read atoms call the isolated resource hook. Filter / collapse / expand stay in the atom.
  • Write atoms call the same hook, then mutate. They import their own modals and services. They return null when the record is not writable.
  • Workflow atoms compose other atoms. They still do not become the light host's entry.
  • Assemblers do not fetch in order to push props down. Chrome unique to that host (a title in a shell header) may read the hook.
  • Scope context carries identity (id, embedded). It does not carry actions, stores, or modal openers.

Several atoms calling the same SWR hook is expected. SWR dedupes on key. Do not lift data to the assembler "to fetch once".

The shared read hook is its own file. A light host imports that file, never a hooks.ts / store barrel that also exports list, document, or send-message.

// any host:
import { useX } from '@/features/Domain/useX';

// never from a light host:
import { useX } from '@/features/Domain/hooks';
import { DomainViewer } from '@/features/Domain';

A flag that only changes behavior of code already in the atom is fine. A flag whose purpose is to skip importing another module is not — that module is a slot or a sibling the assembler omits.

dynamic(() => import(...)) only helps when no static import of that module remains on the light graph.

Assembler

A host is its import list.

// light host — deep imports only
<Scope id={id}>
  <ReadA />
  <ReadB />
</Scope>

// full host — same reads, write atoms in slots, workflows as siblings
<ReadA extra={<WriteA />} />
<ReadB toolbar={<WriteB />} />
<Workflow />
<WriteC />

Route / portal files compose host skeleton (providers, host seams). Do not add a wrapper page whose only job is to sit between the route and the assembler.

The feature barrel may export the full assembler for in-app use. Light hosts must not import that barrel.

Procedure

  1. List hosts (page, portal, share, mobile, popup, micro-app — include the next one).
  2. List capabilities as verbs (view identity, edit field, decide, open host thing). Map host → verbs.
  3. Set grain: each verb a host can skip is an atom. Group the rest under a parent.
  4. Extract primitives and the isolated read hook first.
  5. Extract read atoms. Optional same-domain capabilities become ReactNode slots, not callbacks that close over write modules.
  6. Extract each write / workflow atom into its own file. Heavy deps stay in that file.
  7. Sink state. Move fetch, view-model, transient UI, and handlers out of the assembler. If the page still calls a domain hook to feed props, the split is not done.
  8. Rewrite every host as an import list plus layout. Delete readOnly / isPublic / mode / variant on the fat tree.
  9. Point light hosts at atoms by deep path.
  10. Prove the cut. Grep the light entry's import tree, or trace a module the light host must not ship. A missing button is not proof.
# example: light host must not import a write atom
WORKBENCH_TRACE_MODULE=features/Acceptance/Viewer/Review/AcceptanceDecision bun run build:rr

If the light assembler appears on the importer chain, a static import still exists — usually a barrel, a leftover flag, or actions left on the page.

Example

src/features/Acceptance/Viewer/ is one application of this kit, not the template to copy file-for-file.

HostMounts
In-app Acceptance/index.tsxIdentity, goal, inventory, decision, focus workflow, ledger + write slots
Workbench public detailIdentity, goal, inventory, discussion with sign-in guidance
PortalFull assembler + OriginConversationProvider

Read AcceptanceGoal (read + editSlot) and AcceptanceGoalEdit (write) for the slot cut. Read originConversation.tsx for a host seam. Read workbench AcceptanceDetail.tsx for a light assembler.

Related skills

  • react: single-surface boundaries, styling, memoization.
  • split-micro-app: worker / SSR / gateway / stubs. This skill is how the shared UI is cut afterwards.
  • spa-routes: route files stay thin assemblers.
  • data-fetching-architecture: how the isolated hook fetches, not where a host may import it from.
  • zustand: narrow selectors inside an atom; do not put the whole store on scope context.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

acceptance

FreeSummary

Use this skill to verify a delivery by exercising the real product through a suitable surface such as a CLI, web app, desktop app, or iOS Simulator. It helps create or find a verification plan, capture evidence, and publish an acceptance report.

  • Verifying a completed product change
  • Collecting screenshots or other evidence
  • Creating a verification plan and report
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to add an AI model provider to LobeHub across runtime integration, model cards, settings, branding, documentation, and acceptance checks. It also covers provider documentation updates and distinguishes new providers from models added to an existing provider.

  • Integrating a new AI model provider into LobeHub
  • Updating setup or usage documentation for a provider
  • Checking whether a model needs a new provider integration
lobehub/lobehub83.5KUpdated Oct 26, 2026

add-setting-env

FreeSummary

Use this skill to add a server-side environment variable that sets a default for a user setting. It walks through validation, server configuration and store merging, plus updating the example environment file and self-hosting documentation.

  • Adding an environment variable for a setting default
  • Wiring a new setting domain into server config
  • Documenting a new variable for self-hosting
lobehub/lobehub83.5KUpdated Oct 26, 2026

Provides guidance for registering hooks around an agent’s execution steps, tool calls, human approvals, context compression, and sub-agent calls. Hooks can observe events, supply local mock tool results, or use webhooks to allow or deny tool results under configured policies.

  • Observing agent steps and tool errors
  • Mocking tool results during local execution
  • Requiring webhook decisions before or after a tool call
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-signal

FreeSummary

Build event-driven background workflows for agents by defining sources, interpreting them as signals, and running actions through policies. It also guides asynchronous handoffs, deduplication, scoped execution, completion receipts, and observability.

  • Adding a new event source to an agent workflow
  • Routing interpreted signals to concrete background actions
  • Enqueuing work for asynchronous processing
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to run end-to-end acceptance checks for bot channels in real desktop chat apps. It drives apps such as Discord, Slack, Telegram, WeChat, Lark, and QQ to send a message, wait for a response, and capture the result.

  • Checking a bot response in a real Discord channel
  • Testing chat behavior through Slack or Telegram
  • Capturing a bot interaction in a native macOS app
lobehub/lobehub83.5KUpdated Oct 26, 2026

See all skills by lobehub

Report a problem with this skill