Ask most people what “building on Power Platform” looks like and they’ll describe a canvas app: drag a gallery onto a screen, bind it to a data source, write a bit of Power Fx, ship. That’s a real and useful tool. It is not the only one. For a professional-services firm I built a fleet of full React apps — TypeScript, Fluent UI, real component architecture — running natively on the platform against Dataverse. No separate hosting, no bespoke API, no hand-rolled auth. A lot of Power Platform developers don’t realise this is on the menu.
This is a short field note on how that actually fits together, because the interesting part isn’t “React runs here.” It’s the seam between your code and Dataverse.
Table of contents
Open Table of contents
Canvas apps aren’t the ceiling
Power Apps Code Apps let you take an ordinary React/Vite project and push it onto the platform, where it’s wrapped with Entra ID authentication, DLP and conditional-access policy, and a runtime host — and from your own code you can call the same connectors a canvas app would. You write the app you’d write anywhere; the platform governs and hosts it.
That means the things a canvas app makes genuinely hard — complex client state, a real component library, custom rendering, unit-tested logic — stop being hard, because you’re in React, not in a formula bar. What you give up is the “citizen developer drops controls on a screen” simplicity. For an app with real interaction, that’s a trade worth making.
The read/write split
Here’s the part that matters, and the part that isn’t obvious from the docs. Reads and writes go through different doors.
Reads go through the Code Apps SDK. You get proper OData queries — $select, $filter, $expand — typed, paginated, against your Dataverse tables. This is the well-lit path, and it behaves.
Writes drop down to the Dataverse Web API on the host page, and the detail that trips people up is lookups. You don’t set a lookup by stuffing a GUID into a column; you bind it by navigation property with @odata.bind:
"ts_staffid@odata.bind": "/staffs(<id>)"
Set a lookup the naive way and Dataverse rejects it. Bind it by navigation property and it’s clean. Once that clicks, the pattern is: read through the SDK, write through the Web API, and let Dataverse stay the system of record while the platform owns the token acquisition. You never write an auth line.
What the pattern bought
Abstract patterns are boring without the things they let you build, so — three that a canvas app would have fought me on:
- A weekly timesheet grid that auto-creates the scaffolding rows a user needs before they can log time. It checks an in-memory cache, then Dataverse, then creates the missing rows via
@odata.bind, batching the writes — so the user just sees a grid that works, not a “no timesheet exists yet” dead end. - A client-360 view with real D3 charts for accounts-receivable aging, tabbed across engagements, invoices, and transactions, paginating through thousands of rows without falling over.
- A cross-sell screen that turns a bought-versus-not-bought matrix into one-click opportunity records written straight back into the CRM — analysis and action in the same place, no re-keying into another system.
The quiet win underneath all three: I templatized the read/write plumbing once, so every new page started from the same proven pattern instead of rediscovering the @odata.bind gotcha from scratch. The first page is where you pay the platform-learning tax. Every page after is just React.
When to reach for this
Code Apps aren’t a canvas-app replacement; they’re the tool for when the interaction outgrows the formula bar. If your app is a form over a table, stay in canvas — it’s faster and lighter. If it’s a genuine application — client state, custom rendering, logic you want to unit-test — then a Code App gives you a modern front end without giving up Dataverse as your backend or writing a line of authentication code.
If you’ve been assuming “Power Platform” caps you at canvas apps, it doesn’t. It just moves the hard part from “how do I make this control behave” to “how does my code talk to Dataverse” — and that part, once you’ve drawn the read/write seam, is genuinely pleasant.
Slides: I pulled the core of this into a short deck — download the PDF.
Have you shipped a Code App yet, or is canvas still doing everything you need?