Welcome back!
In the previous chapter, Local JSX Architecture, we discovered that our command logic doesn't print text; it returns a UI element. Specifically, we returned something called <Settings />.
But what is <Settings />? And why didn't we have to write code to draw borders, handle key presses, or create tabs from scratch?
In this chapter, we explore UI Component Compositionβthe art of building complex interfaces by snapping together pre-made blocks.
Imagine you need a new desk for your office. You have two choices:
Building user interfaces (UI) is the same.
If we tried to draw the "Status" screen from scratch in a terminal, we would have to calculate where every single line character (β, β, β) goes. It would be a nightmare.
Instead, our application provides a "Kit" of components.
<Box />.<Text />.<Settings />.
The status command simply composes (uses) the existing <Settings /> component to do its job.
Our goal is to show the "Status" information.
The application already has a generic "Settings" screen that handles:
We want to reuse this screen, but with one specific tweak: We want it to open directly to the "Status" tab.
Let's look at status.tsx again to see how we configure our furniture kit.
First, we grab the pre-made component from our library.
import * as React from 'react';
// We import the pre-built UI block "Settings"
import { Settings } from '../../components/Settings/Settings.js';
When we use the component, we pass it Props (Properties). Think of Props as the instructions included with the furniture kit.
// Inside our call function...
return (
<Settings
onClose={onDone} // Instruction: What to do when user quits
context={context} // Instruction: App data (theme, etc.)
defaultTab="Status" // Instruction: Which tab to open first
/>
);
Explanation:
defaultTab="Status": This is the crucial piece of composition. We are telling the generic Settings component: "You usually start at the top, but for this specific command, please start on the 'Status' page."
When you return <Settings />, you aren't just drawing one thing. You are triggering a chain reaction of composition.
The Settings component is composed of smaller components, which are composed of even smaller ones.
Let's visualize how the data flows when the command runs.
To understand this better, let's pretend we are looking inside the Settings.js file (the component we are importing).
It might look something like this:
// A simplified look INSIDE Settings.js
export function Settings({ defaultTab }) {
// 1. Determine which tab is active based on the prop
const currentTab = defaultTab || 'General';
// 2. Return a composition of smaller parts
return (
<Box borderStyle="round">
<Sidebar active={currentTab} />
<Content tab={currentTab} />
</Box>
);
}
What just happened?
status command passed "Status" into defaultTab.Settings component received it.Settings passed that information down to <Sidebar /> (so it highlights the right button) and <Content /> (so it shows the right text).This architecture separates concerns:
<Settings defaultTab="Status" />). You don't care how the borders are drawn.This means you can create powerful commands in just 3-4 lines of code because you are standing on the shoulders of giants (existing components).
You have now completed the code implementation for the status command!
Settings UI.We have a definition, we have code, and we have a UI. But... we haven't actually pressed "Enter" yet. How does the system coordinate all of these pieces when the user actually types the command?
In the final chapter, we will watch the entire process unfold from start to finish.
Next Chapter: Command Execution Lifecycle
Generated by Code IQ