Bootcamp
Build a simple single-page app in Bubble: groups, conditionals and navigation
Photo by the blowup on Unsplash
Click “Sign up” on a well-built login screen and the form swaps instantly. No spinner, no reload, no waiting. That instant switch is the whole appeal of the single-page app, and in Bubble it’s built from three ingredients: groups, conditionals, and the page URL. This tutorial builds one from scratch, a complete authentication screen with sign up, log in, forgot password and confirm email views, and along the way covers the architectural judgement of when a single-page app is actually the right call.
A single-page app (SPA) is one page containing multiple groups, with exactly one group visible at a time. Conditional logic on each group decides which one shows, so switching between views is near-instantaneous — at the cost of a heavier initial page load.
Why architecture starts with the client-server model
Your Bubble app doesn’t run on your user’s device. It runs on a server (Bubble’s data centres are in the western USA), executing your application logic and reading its database there, while the user’s browser sends and receives data over the internet. If your user is in Australia, every page load, every search, every group that becomes visible and pulls fresh data is a round trip across the Pacific.
Data moves at three moments: when the page loads (the big one; this is where one-second loads and ten-second loads part ways), when data is requested from the database (typically half a second to a second after page load, and again whenever groups appear or data sources change), and when data is written back. The question to hold in your mind constantly as a Bubble developer is: how much data is moving between the client and the server? Page structure is your biggest lever on the first of those moments.
What counts as an acceptable load time depends on the audience. Developers grudgingly tolerate five to ten seconds from back-end tooling; consumers on an e-commerce store will not, and there you’re targeting one to two seconds or less.
Multi-page, single-page or hybrid
Bubble gives you three structural options: an app of multiple pages, each with its own functionality; a single page where groups show and hide; or a hybrid of both.
Multi-page should be your default, for reasons that compound as apps grow:
- Modularity. One page per functional module (dashboard, contacts, deals, settings) keeps element trees readable, expressions simple, and the debugger usable.
- Faster individual loads. Less on each page means less to transfer; users typically see a page in one to two seconds.
- Metadata. Pages carry titles, descriptions and social images, the raw material of SEO. They appear in your sitemap, and show up as distinct entries in Google Analytics so you can actually read engagement.
- Workload control. A page dense with elements may be quietly loading data, evaluating show/hide conditionals, and running redirects on every load. Even expression phrasing matters: on a big dataset,
Do a search for...:first item is empty,Do a search for...:count is 0and0 is Do a search for...:countproduce the same answer at wildly different workload costs, roughly fifteen units, three, and 0.2 in one well-known forum comparison. - Native browser controls. When the URL changes, back, forward and reload just work. Users expect this; apps that break it don’t feel professionally built.
The single-page app’s one great advantage is speed of switching: load everything once, then move between views instantly. That makes SPAs the right choice for simple screens that benefit from immediate toggling: an auth screen (login, sign up, forgot password), an onboarding flow (step one, two, three), a settings screen (profile, credentials, notifications), or a dashboard with a few views. In practice almost every real app is a hybrid; the judgement is knowing which sections earn the SPA treatment. When in doubt, do a competitor analysis. Look at how industry-leading apps like Xero structure their URLs and you’ll notice mature products lean firmly multi-page, with list views and detail views on separate addresses.
An auth screen sits squarely in SPA territory. Its forms all link to each other, the workflows are simple, and the switching should feel instant. So that’s what we’ll build. (For the broader theory, the module overview on application architecture covers the full decision framework.)
How the mechanism works
Before the clicks, hold the design in your mind. We’ll create a page called auth and a URL parameter called form, taking values like login, signup and forgotpw. On the page sit four groups, one per form. Each group has a conditional: show me when the URL parameter names me. And inside each group, a text (“Need an account? Sign up”) navigates to the same page with a different parameter value.
That’s the whole loop: click a text, the parameter changes, the conditionals re-evaluate, a different group appears. Helpfully, when Bubble navigates to the same page with new parameters it doesn’t do a full page reload; it just updates the URL and the logic, which is what makes the switch feel instantaneous.
Step 1: create the page and the groups
- Create a new page and call it
auth. - Set the page defaults: give it a page title, set the layout to Align to parent, width 1440, minimum height 800. (The minimum height barely matters; content will push the height down.) Give the page a dark background so the forms stand out.
- Align to parent divides the page into nine positions. Not quadrants but a “nonent”, a word you only ever learn through Bubble. Draw a group in the centre position and name it
Group Sign Up. - Style the group: white flat background, 8-pixel radius. Detach the style and save it as a reusable style called
Group Auth Containerwith padding of 32 at the bottom and 56 on the sides. You’ll want it for every auth form. - Set the group’s layout to column (its contents run down the page) with a max width of 472.
- Inside it, build a header: a row, full width, height 80, holding your logo and a material icon (
close) at 24 by 24, right-aligned, with padding of 16 top and bottom and 24 on the sides. Below the header, add aGroup Formcolumn with the 32/56 padding to hold the inputs and CTA buttons. - Copy and paste the group three times and rename the copies
Group Login,Group Forgot PasswordandGroup Confirm Email. All columns, all sitting in the same centre position, stacked on top of each other.
A practical note from doing this at speed: it is very easy to paste an element into the wrong group. If things go missing, hide everything except the group you’re working on via the element tree, and drag elements to where they belong. And if a group turns out to be doing nothing, delete it; unnecessary groups make everything messy and quietly introduce new problems.
Step 2: add the navigation texts and workflows
Give each group a heading so you can tell them apart, then add the cross-links: on sign up, “Already have an account? Log in”; on login, “Need an account? Sign up”; plus forgot password and return-to-login texts. (While you’re there: it’s “log in” as the verb, “login” as the noun. Being particular pays.)
Now wire each text to a workflow:
- On the sign-up text, choose Add workflow, then the Go to page action (under Navigation).
- Set the destination to
auth, the same page we’re on. - Tick Send more parameters to page and add the key
formwith the valuesignup. Bubble will try to offer dynamic data for the value; click out of the field, click back in, and type the plain text. - Repeat for every navigation text:
form = login,form = forgotpw, and so on. This build needs five workflows in total.
To check you haven’t missed one, turn on Show element labels in the editor’s view menu. Texts that already have workflows are marked, so the one you skipped stands out.
Preview the page and click a text: nothing visibly changes yet, but watch the URL. Bubble appends form=signup to the address. (You’ll also notice debug_mode=true in the preview URL; that’s what gives you the debugger bar, and adding it by hand to any URL lets you step through workflows one action at a time: Go to page auth, key form = signup, run next.)
Step 3: show and hide with conditionals
Now the groups need their logic. For each of the four groups:
- In the Layout tab, set This element is visible on page load to no, and Collapse when hidden to yes. With align-to-parent the collapse isn’t strictly needed, but do it anyway; it’s the habit that saves you when you’re working in rows and columns.
- In the Conditional tab, add: When
Get data from page URL(parameter nameform, type text)is "login"for the login group, this element is visible. - If the expression won’t accept your typed value, click out of it and back in. Bubble sometimes needs that nudge to treat it as literal text rather than an expression. When the condition turns blue, it’s evaluating correctly.
- Don’t rebuild the condition three more times. Right-click it, copy condition, then on each remaining group paste condition and change only the value:
signup,forgotpw,confirmemail.
Preview again, this time with ?form=login on the URL. The login form shows. Click “Need an account? Sign up” and the sign-up form swaps in instantly. Forgot password works the same way. The confirm-email group has no navigation text pointing at it; that one gets shown by an action in the sign-up workflow later, which is exactly the kind of screen that suits it.
Step 4: handle the empty URL with a redirect
There’s one hole: visit plain /auth with no parameter and you get a blank page, because no group’s condition is true. The fix is a page-load redirect:
- Go to the Workflows tab and add an event: General — Page is loaded.
- Set Only when to
Get form from page URL is empty(click out of the field to lock inis emptyrather than a comparison). - Add one action: Go to page
auth, sendingform = login. - File the workflow in a folder called
Redirects, so future-you can find it.
Now stripping the parameter off the URL always lands the user on the login form.
Resist the urge to add many of these. A page thick with redirects evaluates every one of those conditions on every load: confusing to manage, expensive in workload units, and miserable to debug, because stepping through workflows means wading past redirect noise first. Worst case, two redirects with crossing logic change a value back and forth forever and you’ve built an infinite loop. Keep redirects to one or two per page, consolidate multiple checks into a single workflow with conditional steps where you can, and map them out on a whiteboard if the logic starts to overlap.
Try it yourself
Extend the build: recreate the four-form auth screen in your own app, then test it like a sceptical user. Reload mid-flow. Do you stay on the right form? Use the browser back button. Does it walk you back through the forms you visited? Because state lives in the URL rather than in hidden page memory, both behaviours come free. That’s the professional finish this pattern buys you.
This skill in the AI era
Single-page versus multi-page never went away; it’s the same architectural fork React, Next.js and Astro developers argue about today, with the same trade-offs of instant switching against load weight and URL semantics. When we scope an app build with Claude Code, the page map and the “what deserves SPA treatment” call are decisions we make before any code is generated, exactly as this module taught planning before building. The architecture instincts are the durable asset; the tooling underneath them changed. If you’re planning an app of your own, that’s the thinking we bring to every AI-assisted build.
If this sparked something, let's talk.
No pitch, no pressure — just a conversation about what you're working on.
Let's talkRelated posts
Bootcamp
A simple approach to data modelling
A four-step method for designing a database schema: identify entities, map relationships, choose fields. Taught through the CRM data model built in Bubble.
Bootcamp
Module 5 — Application architecture: pages, states and reusable pieces
Single-page vs multi-page apps, URL parameters, custom states, reusable elements — and a big practical building a main template with an expanding navigation drawer.
Bootcamp
API calls as actions vs data (and the planning checklist)
When to use Bubble API calls as actions versus data sources, a one-to-many auth pattern, and the planning checklist that keeps integrations from failing silently.