Skip to content
Back to the Bootcamp
The Momentum Bubble Bootcamp, free. Part 5 of the paid course that trained hundreds of Bubble developers — released free, because the thinking it teaches is exactly what great AI-assisted development is built on.

Bootcamp

Build a navigation drawer in Bubble properly

Every SaaS product you admire has the same skeleton: a header across the top, a sidebar down the left, and a content area that does the actual work. The sidebar earns a more specific name, a navigation drawer, because it moves between a full panel of labelled options and a slim rail of icons. Building one properly in Bubble touches nearly every intermediate skill at once. This is the full build, in order, with the mistakes left in where they teach something.

A navigation drawer is a sidebar that can expand and collapse — sliding from full labelled navigation down to an icons-only rail — so the user trades navigation detail for content space on demand.

The page skeleton: header, drawer, content

Start with the containers; everything else hangs off them.

  1. Create a new page called Template (watch the dialog; it’s surprisingly easy to accidentally create a reusable element instead of a page).
  2. Set the page layout to column at the standard 1440 width. The page stacks two rows: a header, then a container row split into left (drawer) and right (content).
  3. Add Group Header: a row, full width, fixed height 64, padding 24 on the sides. Drop in a logo (a fixed shape at 168 by 27 is fine for now) and align it vertically. Don’t set the header to fit width to content or it will simply collapse around the logo.
  4. Add Group Container below it: a row, variable width with no minimum and fit-width off, so it spans the full screen.
  5. Inside the container, add two groups. Group Nav Drawer is a column (its contents run down the page) with a max width of 256, the expanded drawer width. Group Content is also a column: no minimum width, no fit-width, so it takes all remaining space; padding 16 on top and 28 on each side; background a shade of off-white so the white drawer and header read as distinct surfaces.

Use placeholders freely. A grey shape where the chart will go keeps you moving; don’t get distracted by colours and pictures while the structure is unsettled.

Fill the drawer

The drawer holds a user row, the main nav options, a logout row, and legal links.

  1. User row. A row group (Group User) with padding 24, containing a 48-by-48 profile image placeholder and an expand/collapse icon. Set the row’s alignment to space between, which pushes the avatar and the icon to opposite ends.
  2. Nav options. Build one row first: Group Contacts, a row with padding 16 top and bottom, 24 on the sides, height 72, no fixed width, fit height to content on. Inside: a material icon in a 32-by-32 box, and a label using your design system’s Body Large bold text style. (This is where a design system pays for itself; the style is just there, no hunting for font settings.) Set a column gap of 24 between icon and label, fit the text’s width to content, and vertically centre both. Finish with a bottom border: 1 pixel, solid, in a light primary variant. Define primary 100 in your styles if it doesn’t exist yet.
  3. Material icon names take a moment of honesty: the avatar icon is actually called person, and the deals icon is checklist. Copy the contacts row twice, rename to Group Deals and Group Settings, and swap the icons and labels.
  4. Select the three rows (hold shift), right-click, and group elements into a column container named Group Nav Options. Give the drawer a row gap of 8 so the options have the small breathing space the design shows.
  5. Logout. Structurally identical to the other rows, so copy the settings row, rename it Group Logout, and change the icon and label.
  6. Terms and privacy. Two texts styled as body small links: underlined, grey 500, size 14. Let each text wrap its content (no fixed width, no minimum height, fit width to content), then group the pair into a small column with a row gap of 16 and the drawer’s side padding.

Watch the element tree as you go. Pasted elements routinely land in the wrong container, and the tree is where you catch it. Drag things to where they belong and name every group properly (Group Deals, not Group A) while you’re there.

Make the drawer full height

Preview at this point and the drawer stops where its content stops, short of the bottom of the screen. The naive fix, minimum height 100 per cent on the container row, measures against the whole page and gifts you a permanent scrollbar. The clean approach restructures slightly:

  1. Select Group Header and Group Container, right-click, and group into a column container. Call it Group Main.
  2. Set Group Main to fixed height, 100%. (Mind which field you’re editing; it’s easy to set the width to 100 per cent and undo your way back.)
  3. On Group Container, remove fit height to content so it expands to fill whatever vertical space the header leaves behind.

Now the header takes its 64 pixels and the container, drawer included, stretches to fill the rest of the viewport with no scrollbar.

Pin the bottom group with space between

The design wants the user row and nav options at the top of the drawer, with logout, terms and privacy anchored at the bottom. Among the container alignment options (top, middle, bottom, space between, space around), the answer is sitting there.

  1. Group Group Logout and the terms/privacy column into one column called Group Bottom. (To select elements buried under others, hold command or control and click through the layers, then shift-click to add to the selection.)
  2. Group Group User and Group Nav Options into Group Top, and re-add the 8-pixel row gap the regrouping just swallowed.
  3. On the drawer itself, set the container alignment to space between. With the parent chain set to full height, the drawer pushes Group Top up, Group Bottom down, and distributes the empty space between them.

Preview it, dropping debug_mode=true off the URL first since the debugger bar distorts the layout. The bottom group hugs the bottom edge, exactly as designed.

Drop shadows on header and drawer

The design separates the surfaces with a soft shadow rather than hard borders. On the header’s Appearance tab, add a box shadow: outset, X offset 2, Y offset 7, blur 12, colour #222222 at 8 per cent opacity. Apply the identical shadow to the drawer. Combined with the off-white content background, the header and drawer now sit visibly “above” the page; a small touch that does a lot of the differentiation work.

Expand and collapse with a custom state

Now the drawer earns its name. Collapsed, it’s 96 pixels wide: profile picture, labels, terms and privacy disappear, leaving centred icons.

  1. Create the state. On the drawer group, add a custom state called expand, type yes/no, default yes.
  2. Toggle it. On the expand icon’s click workflow, add two Set state actions: one with the condition only when Drawer's expand is yes that sets it to no, and one with only when Drawer's expand is no that sets it to yes. The condition goes on each action, because evaluated in sequence the first change would immediately invalidate the second check and the toggle would do nothing.
  3. Swap the icon. Conditional on the icon: when Drawer's expand is no, change the icon to the right-pointing arrow and its size to 32, matching the nav icons.
  4. Hide the labels. On each text, the profile picture, and the terms/privacy group: when expand is no, this element is not visible, with collapse when hidden on and no minimum width, so each row closes around its icon. Bubble distinguishes copying a condition from copying an expression, so right-click, copy condition, and paste it across all the elements that hide.
  5. Put those conditions on the right elements. The instructive mistake in the original build: the hide conditions went onto the icons instead of the texts, neatly hiding the icons and keeping the labels. The element tree tells you what you attached things to; the preview, reloaded after edits, tells you the truth.
  6. Shrink the drawer. Add a condition on the drawer: when expand is no, max width 96.
  7. Fix the spacing. Collapsing widths exposes loose height settings; rows that were happy at 72 pixels start stretching. Use the debugger’s Inspect tool to compare a group’s declared min height against its actual rendered height, then fix the heights: the nav rows to a fixed 72, the user group to a fixed 96. It’s fine to use fixed heights when things genuinely need to be fixed.
  8. Centre the icons. In the collapsed state the icons sit off-centre. Add a condition to each row: when expand is no, container alignment middle.

Test the toggle: panel to rail and back, labels folding away, icons centring. Professional drawer behaviour, powered by one yes/no state.

Convert to reusable elements without losing the shadow

The header and drawer belong on every logged-in page, which makes them textbook reusable elements: build once, maintain in one place, drop onto any page. But the obvious move has a catch. The drop shadow spills outside the element’s bounds, and a reusable clips at its own edges, so converting directly loses the shadow. The workaround is to convert the contents rather than the container:

  1. On the header, group its contents into an inner group, convert that inner group to a reusable element called Header, and leave the shadow on the outer group that stays on the page. Drop the reusable into place and delete the original contents; edit the reusable and any change appears wherever it’s used.
  2. The drawer is the same move with more baggage. Wrap it in an outer Group Drawer Container, transfer the outer styling (shadow, min height 100%, max width 256) onto the container, then convert the inner drawer to a reusable.
  3. Expect breakage, and don’t panic at it. Converting the drawer takes its workflows along, which is what you want, but orphans every expression that referenced the old group name: twenty-four issues, in the original build. They’re all the same issue. Repoint each expression at the reusable (NavDrawer's expand) and they come good; if it goes truly wrong, the back button undoes the conversion. The editor can render stale after this kind of surgery, so refresh it.
  4. One real problem remains: the outer container on the page no longer collapses, because its max-width condition referenced the old group. The fix demonstrates something genuinely useful: you can reference a reusable’s custom state from outside it. On the drawer container, add the condition: when NavDrawer's expand is no, max width 96. The page-level container now collapses in sync with the state living inside the reusable.

Reusables are worth this occasional friction: duplicating a workflow in three places is how you earn the bug you only fix in two. Just don’t over-modularise; deeply nested reusables make an app harder to understand, and the detach option exists for walking it back.

Highlight the active page with a condition-holding group

Last detail: the nav row for the current page should be highlighted, with a tinted background and primary-coloured icon and label. The obvious approach puts a condition on the row (when Current page name is template, background colour primary 100), but the icon and the text each need the same check, which means one expression maintained in three places.

The improvement is a small pattern worth stealing: use the group to hold the condition, and let its children read the answer.

  1. Change the nav row group’s type of content to yes/no, and set its data source to the check itself: Current page name is template.
  2. On the group: when this group’s yes/no is yes, show the background colour (primary 100).
  3. On the text: when parent group’s yes/no is yes, text colour primary.
  4. On the icon: same condition, icon colour primary.

The logic now lives in exactly one place; three elements refer to it. As you build the contacts, deals and settings pages, each row’s data source names its own page, and the highlight follows the user around the app.

Try it yourself

Rebuild the drawer in your own app, then document it the way a professional team would: a page-architecture diagram of the header, drawer and content containers, alongside your app’s sitemap of logged-in and logged-out pages. Ten minutes in Lucidchart, and anyone joining the project understands the structure without auditing the editor.

This skill in the AI era

This build is component thinking, learnt visually: state owned in one place, children reading from a single source of truth, presentation wrapped around reusable internals. Those are precisely the ideas behind React components, props and context. When we direct Claude Code to build a sidebar today, the collapse state, the active-route highlight and the layout contract are specified in a sentence each, and the component appears in minutes; knowing what to specify is the transferable skill, as we found moving from Bubble to AI-assisted coding. It’s the kind of fluency we build with teams in Claude Code training.

If this sparked something, let's talk.

No pitch, no pressure — just a conversation about what you're working on.

Let's talk
Share: