Published 3/8/20266 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:

  1. No collisions with variables (
    var*
    ), collections (
    col*
    ), or component properties
  2. Instant autocomplete — type
    nf
    in any property field and Studio shows your entire design system
  3. Readability — anyone opening the app knows exactly what
    nfColors.primary
    is

Here's the hierarchy:

FormulaWhat It Does
nfPalette.*
Hex color strings (conditional light/dark)
nfColors.*
Color values derived from palette
nfTokens.*
Spacing, radius, font size scales
nfType.*
Typography (size, weight, color, hex)
nfHeights.*
Responsive layout heights
nfIcons.*
Theme-aware SVG data URIs
nfIsMobile
Boolean breakpoints
nfStatusBg()
Status color helpers

Two-Layer Color System

Colors have two layers because Power Apps needs both:

Layer 1 —

nfPalette
: Hex strings. Used in SVG data URIs and anywhere you need a CSS-style color string.

Layer 2 —

nfColors
: Actual Color values converted with
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:

  1. Load config from a SharePoint/Dataverse config list
  2. Restore theme from
    LoadData()
    (local persistence)
  3. Check permissions (admin role gating)
  4. 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.

How I Structure Every Power Apps Project — Power Apps UI