Published 3/8/2026 • 6 min read
How I Structure Every Power Apps Project
Named Formulas, design tokens, responsive breakpoints, and a declarative architecture that makes Power Apps feel like a real dev framework.
Every Power Apps project I build follows the same structure. Not because I'm rigid — because it works. After building dozens of production apps, I've landed on a pattern that gives me theme switching, responsive layouts, and consistent design without fighting the platform.
Here's the full breakdown.
The Problem With Most Power Apps Projects
Most Power Apps I've inherited look the same: RGBA values hardcoded on every control,
App.OnStart running 40 lines of imperative Set() calls, no consistency between screens, and the moment someone asks "can we add dark mode?" the answer is "not without touching 200 properties."
This isn't a Power Apps problem. It's an architecture problem. The platform gives you Named Formulas, records, responsive breakpoints, and data URIs. Most people just don't use them.
Named Formulas Over Variables
The foundation of everything is Named Formulas. Unlike
Set() variables that run imperatively in OnStart, Named Formulas are declarative. You define what they equal, and Power Apps recalculates them when their dependencies change.
// This recalculates automatically when varThemeName changes nfThemeName = Coalesce(varThemeName, "Light"); nfIsLight = nfThemeName = "Light";
The key insight: you only need one variable (
varThemeName) to drive your entire design system. Everything else is a Named Formula that derives from it.
The nf* Convention
Every Named Formula starts with
nf. This gives you three things:
- No collisions with variables (
), collections (var*
), or component propertiescol* - Instant autocomplete — type
in any property field and Studio shows your entire design systemnf - Readability — anyone opening the app knows exactly what
isnfColors.primary
Here's the hierarchy:
| Formula | What It Does |
|---|---|
| Hex color strings (conditional light/dark) |
| Color values derived from palette |
| Spacing, radius, font size scales |
| Typography (size, weight, color, hex) |
| Responsive layout heights |
| Theme-aware SVG data URIs |
| Boolean breakpoints |
| Status color helpers |
Two-Layer Color System
Colors have two layers because Power Apps needs both:
Layer 1 —
: Hex strings. Used in SVG data URIs and anywhere you need a CSS-style color string.nfPalette
Layer 2 —
: Actual Color values converted with nfColors
ColorValue(). Used in Fill, FontColor, BorderColor.
nfHex(h: Text): Color = ColorValue(h); nfPalette = If(nfIsLight, { pageBg: "#F9FAFB", primary: "#2563EB", text: "#111827" }, { pageBg: "#111827", primary: "#60A5FA", text: "#FFFFFF" } ); nfColors = { pageBg: nfHex(nfPalette.pageBg), primary: nfHex(nfPalette.primary), text: nfHex(nfPalette.text) };
When
varThemeName changes, nfIsLight flips, nfPalette recalculates, and every nfColors.* reference across your entire app updates. One variable. Zero manual work.
Spacing Tokens
I use the same spacing scale on every project:
nfTokens = { space: { xs: 4, sm: 8, md: 12, lg: 16, xl: 24, x2: 32 }, radius: { sm: 4, md: 8, lg: 12, xl: 16, pill: 999 } };
Every container in the app uses
nfTokens.space.xl instead of 24. Why? Because if the design ever needs tighter spacing, I change one number. More importantly, it makes the code self-documenting. PaddingLeft: =nfTokens.space.xl tells you it's "extra large padding" without checking what 24 means.
Typography That Covers Both Control Types
Power Apps has modern controls (
TextCanvas.Weight) and classic controls (FontWeight). The typography system handles both:
nfType = { h1: { size: 28, weight: 'TextCanvas.Weight'.Semibold, // Modern fontWeight: FontWeight.Semibold, // Classic color: nfColors.text, css: nfPalette.text // For SVGs } };
The
css property is the hex string — it gets injected into SVG data URIs so icons match the text color automatically.
Responsive Without Complexity
Three Named Formulas replace every inline
App.Width check:
nfIsMobile = App.Width < 640; nfIsTablet = App.Width >= 640 && App.Width < 1024; nfIsDesktop = App.Width >= 1024;
Then I use them everywhere:
PaddingLeft: =If(nfIsMobile, nfTokens.space.sm, nfTokens.space.xl) Visible: =nfIsDesktop Height: =If(nfIsMobile, 56, 72)
Layout heights that change with screen size get their own formula:
nfHeights = { topBar: If(nfIsMobile, 56, 72), toolbar: If(nfIsMobile, 52, 60) };
Theme-Aware SVG Icons
This is where it gets interesting. Power Apps Image controls accept SVG data URIs. If you inject the theme color into the SVG, the icon changes color when the theme switches:
nfIcons = { search: "data:image/svg+xml;utf8," & EncodeUrl( "<svg xmlns='...' viewBox='0 0 24 24' fill='none'> <path stroke='" & nfType.label.css & "' stroke-width='1.5' d='M21 21l-4.35-4.35M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z'/></svg>" ) };
nfType.label.css is the hex string for the label color. In light mode it's #4B5563 (dark gray), in dark mode it's #D1D5DB (light gray). The SVG rebuilds automatically.
I have 30+ icons defined this way. Set the Image property to
nfIcons.search and never think about it again.
Status Helpers
Every app has status pills. Instead of writing Switch statements on every status container, I use Named Formula functions:
nfStatusBg(s: Text): Color = Switch(Lower(Trim(Coalesce(s, ""))), "active", nfColors.stActiveBg, "in maintenance", nfColors.stMaintBg, "retired", nfColors.stRetiredBg, nfColors.secondaryBg );
Usage on a gallery item is just two lines:
Fill: =nfStatusBg(ThisItem.Status) FontColor: =nfStatusFg(ThisItem.Status)
Theme-aware, null-safe, and DRY.
What OnStart Actually Does
With the design system in Named Formulas,
App.OnStart is short and focused on runtime setup:
- Load config from a SharePoint/Dataverse config list
- Restore theme from
(local persistence)LoadData() - Check permissions (admin role gating)
- Set nav state (drawer closed, debounce timer)
That's it. No color definitions, no layout constants, no icon strings. Those all live in Named Formulas where they belong.
The Result
Every screen in the app looks like this:
Screen.Fill: =nfColors.pageBg Container: PaddingLeft: =If(nfIsMobile, nfTokens.space.sm, nfTokens.space.xl) Fill: =nfColors.cardBg BorderColor: =nfColors.border RadiusTopLeft: =nfTokens.radius.lg Title: Size: =nfType.h1.size Weight: =nfType.h1.weight FontColor: =nfType.h1.color
Zero hardcoded values. Full theme support. Responsive. And if you've used this pattern on one project, you carry it to every project.
Get the Full Code
I've published the complete Named Formulas starter code — palette, colors, tokens, typography, breakpoints, icons, and status helpers — on PowerApps UI. Copy it into your app's Formulas property and start building.
The component library at powerappsui.com is designed to work with this design system. Every component accepts theme config that maps directly to
nfColors and nfTokens.
This is the architecture behind every app I build. If you have questions or want to see how specific parts work, suggest an idea or find me on Reddit at r/PowerApps.