Layout with Box

Build the dashboard shell with Flexbox in cells: a top bar, a sidebar and cards.

This chapter's app is running in your console. Open it with ⌘⌥JCtrl+Shift+J, then try it.
Watch the chapter3:40 · narrated · captions

In this chapter

  • A top bar
  • Sidebar and cards
  • Responsive by default

The console is a grid of cells. Text.js lays it out with Yoga, the same Flexbox engine React Native uses, so <Box> follows the CSS flexbox rules. Units are cells: p-1 is one column and one row of padding.

A top bar

Most screens are a column: a one-row bar, then everything else. Give the bar a fixed height with h-1 and let the content take the rest with grow.

app/page.tsx+8-4tsx
Removed: import { Box, Text, useInput } from "@textjs/next";Added: import { Box, Spacer, Text, useInput } from "@textjs/next";export default function Page() {  return (Removed:     <Box className="flex-col border rounded px-2 py-1">Removed:       <Text bold>▼ Acme Deploys</Text>Removed:       <Text color="muted">Hello, console.</Text>Added:     <Box className="flex-col grow">Added:       <Box className="flex-row h-1 px-1 gap-2 shrink-0">Added:         <Text bold>▼ Acme</Text>Added:         <Text color="subtle">acme.local</Text>Added:         <Spacer />Added:       </Box>Added:       <Box className="flex-row grow">{/* sidebar + cards */}</Box>    </Box>  );}

<Spacer> is a growing box: anything after it is pushed to the right edge.

A fixed-width sidebar with a right border, and three cards that share the remaining width. basis-0 with grow gives each card an equal share regardless of its content.

app/page.tsx+31-1tsx
Added: const NAV = ["Overview", "Deployments", "Settings"];Added: Added: function Card({ title, value }: { title: string; value: string }) {Added:   return (Added:     <Box className="flex-col border rounded px-1 grow shrink basis-0" minWidth={14} minHeight={4}>Added:       <Text color="subtle">{title}</Text>Added:       <Text bold>{value}</Text>Added:     </Box>Added:   );Added: }Added: export default function Page() {Added:   const [sidebar, setSidebar] = useState(true);Added:   const [stacked, setStacked] = useState(false);Added: Added:   useInput((e) => {Added:     if (e.input === "s") setSidebar((v) => !v);Added:     if (e.input === "d") setStacked((v) => !v);Added:   });Added:   return (    <Box className="flex-col grow">      {/* top bar */}Removed:       <Box className="flex-row grow">{/* sidebar + cards */}</Box>Added:       <Box className="flex-row grow">Added:         {sidebar && (Added:           <Box className="flex-col w-18 shrink-0 border-r px-1 pt-1">Added:             {NAV.map((item) => <Text key={item}>{item}</Text>)}Added:           </Box>Added:         )}Added:         <Box className={stacked ? "flex-col grow gap-1 p-1" : "flex-row grow items-start gap-1 p-1"}>Added:           <Card title="Production" value="acme-web" />Added:           <Card title="Deployments" value="6 today" />Added:           <Card title="Build time" value="38s p50" />Added:         </Box>Added:       </Box>    </Box>  );}

Press s and d in the console. Yoga recomputes the layout and only the rows that changed are repainted, so toggling the sidebar costs a single console.log.

Responsive by default

The grid follows the docked DevTools panel. Drag the console wider or taller and the cards reflow. When you need breakpoints, useBreakpoint() returns the current one, in cells.

Try it

This is Acme Deploys at the end of the chapter, running in DevTools. Click the pad, then use the keys below.

▼ 02 · Layout with Box

Open the console

⌘⌥J
CtrlShiftJ

The app renders in DevTools. Then click here and press s.

SsidebarDdirection

Open on a desktop

This demo renders in the Chrome DevTools console, which needs a desktop browser.

Quiz

2 questions

Answer every question to complete the chapter.

In className utilities, what does p-1 mean?

 

Which class lets a box take the remaining space?