Once UI in practice: how a backend engineer finally builds frontends he wants to look at
I was never a designer. Tables, forms, service layers, fine. But the moment it came to spacing, hierarchy, and colour, I started guessing. With Once UI that guessing stopped overnight.
I was never a designer. Seven years of Java, Spring, databases, and architecture: anything that lives behind a surface, I could do. The moment it became about the surface, the awkward guessing started: are 16 pixels too many here? Should the button be rounded or square? Which grey doesn't look cheap? The frontends I built in my first six years worked, but I rarely enjoyed looking at them.
Three weeks ago I migrated this site from a Vite + Tailwind combination to Next.js 16 with Once UI. Only then did I realise what I had been missing all along: not an eye for design, but a system that pre-decided the calls I'm not competent to make.
What Once UI is, and what it isn't
Once UI is a React/Next.js component library from the Once Magazine team. It ships geometric primitives (<Column>, <Row>, <Grid>), content components (<Heading>, <Text>, <Button>, <Card>), and a theming system built on CSS custom properties. It's actively developed. I moved from 1.6 to 1.7 mid-migration and nothing broke.
Important to understand: Once UI is not a page builder and not an all-in-one kit like Material UI. If you need filterable tables, datepicker dialogs, or complex forms, you're building them yourself or pairing with a second library. What Once UI gives you is exactly what I had been missing, a grammar for the surface.
What makes the difference as a backend engineer
Spacing tokens. Spacing in Once UI is a discrete scale: 0, 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 104, 128, 160. Write gap="6" and you get a TypeScript error: doesn't exist. At first this strictness annoyed me. Then I realised what it actually does: it removes the only call I always lost on when building frontends: which number is the right one. The answer is: one of the allowed ones. Done.
Theming as a data attribute. A single data-brand and data-accent attribute on <html> swaps the entire colour palette. The two colours of this site (blue as brand, cyan as accent) are defined once in the configuration file and referenced throughout the codebase via --brand-on-background-strong, --brand-alpha-weak, and similar custom properties. Want the whole site green tomorrow? One-line change.
The layout primitives. Once UI skips Tailwind-style utility classes. Instead I write markup like this:
<Column gap="24" padding="32" maxWidth="m">
<Heading variant="display-strong-l">Headline</Heading>
<Text onBackground="neutral-medium">Description</Text>
</Column>That's no longer flex flex-col items-stretch gap-6 p-8 max-w-2xl, it's structured content. After three days I'm writing frontends I can read without opening a stylesheet next to them.
Where it still chafed
Honestly, because anyone else migrating will hit the same bumps:
- Responsive props aren't reactive everywhere. The
hideprop and similar conditional props work, but at breakpoints that didn't always match my expectations. For the header switch between desktop and mobile, I ended up writing plain CSS media queries. Faster than reverse-engineering the library internals. - Theme init without FOUC takes care. To stop dark mode from flashing light for 200 ms, a tiny inline script must run before hydration. The recommended place is the root layout, not the nested locale layout I tried first. A full walkthrough of that stumble is in my migration post (linked below).
- Learn the spacing scale. For three days I checked the TypeScript error list every other
gapvalue to remember which step was next. Now the scale lives in my head.
The big win: I trust the result
Before Once UI I built frontends I hoped someone would like, but rarely felt confident about. Today I send mockups and don't think "is this too much padding?" or "do these colours look cheap?". The guardrails are built in. A layout built in Once UI with the standard token scale doesn't look like a hobby build.
For a backend engineer that's a release. I can focus on what I can do (architecture, data, logic) and the frontend carries itself, without me simulating designer intuition for two days per section.
Who Once UI is for
Exactly people like me: backend or full-stack engineers who want to build a marketing site, a portfolio, or a product surface without drifting into the designer role. It saves the mechanical work (layout, spacing, theme) without forcing you into a visual identity that isn't yours.
The site you're reading is the reference. If you stop scrolling for a moment (Hero, Audiences spotlight, Pricing cards) you're looking at Once UI plus a layer of my own SCSS modules. That's all it is, and for someone without a design background it's the most honest tool I've ever worked with.
If you want the concrete migration story (what actually broke when moving from Vite + Tailwind to Next.js 16 with Once UI) head to the migration post. That covers the how. This one was about the why.
Update, June 2026: now on 1.7.10
I wrote this post in May. The 1.7 line has developed steadily since then, and I am now on version 1.7.10. The updates have been pleasantly unspectacular: heavyweights like Recharts and PrismJS became optional dependencies, new components arrived, and not one of those updates broke my build. That is exactly what I want from a design system I rely on: small, continuous updates I can adopt without turning each one into its own migration project.
The real stress test, though, was not this portfolio. It was Aveiro: a complete AI publishing platform that I have built entirely on Once UI since the original post. The whole dashboard, the visual editor, every toolbar and panel, spread across roughly 960 files. That a small marketing site is pleasant to build this way was expected. That a product this size runs stably on it is the real proof. And it does.