Skip to content
duarteclemente
Go back

Five Gotchas That Make a Power Apps Code App Actually Run

Power Apps Code Apps let you ship a normal React/Vite app onto Power Platform and call its connectors from your own code. No canvas, no formula bar — just a build output the platform wraps with auth, governance, and a runtime host. It feels like plain React. Then you deploy, and the thing that ran perfectly on localhost is a blank white rectangle inside the host.

I built a document manager for a professional-services firm on Code Apps. It’s genuinely good tech. But five runtime quirks stood between “works locally” and “works in production,” and none of them are in a getting-started guide. Each one cost me an afternoon of staring at a blank screen. Here they are so they don’t cost you one.

Table of contents

Open Table of contents

1. Import the generated service files dynamically

The platform generates service files for your connectors and SDK wiring. The obvious move is to import them at the top of your module like anything else. Do that and the app crashes before the SDK even boots — the generated code runs at import time and assumes a runtime context that doesn’t exist yet.

The fix is to import() them dynamically, after the host is ready, not statically at the top of the file. Once the SDK has initialised, you pull the service module in and it behaves. It’s a one-line change that looks like a style preference and is actually load-bearing.

2. Use hash-based routing, not browser history

Your app isn’t served from a clean root URL. It’s served from a deep proxy URL buried inside the Power Platform host. Normal browser routing — the history API that React Router reaches for by default — assumes it owns the path and can push clean URLs onto it. Inside the host, it can’t. Refresh a route and you get a 404 from a server that has never heard of your /documents/42 path.

Switch to hash-based routing. The # fragment stays client-side, the proxy never sees it, and every deep link and refresh survives. This is the difference between a demo that works if you never touch the address bar and an app people can actually bookmark.

3. The native Dataverse SDK returns empty arrays — go through the connector

This is the one that nearly broke me, because it fails silently. No error, no warning, no rejected promise. The native Dataverse SDK returned an empty array for every table I queried in this environment. My code was correct, my query was correct, the tables had data — and I got [] back every single time, cheerfully, with a 200.

You debug a thrown error. You do not debug a successful call that returns nothing, because nothing tells you it’s wrong. I lost real time assuming my filters were off before I accepted the SDK path itself was the problem. The answer was to stop using the native SDK for reads and go through the connector instead. Same data, through a different door, and suddenly every table came back populated. If your rows are vanishing without a trace, this is where I’d look first.

4. One orphaned connection reference breaks auth for everything

Code Apps authenticate through connection references. You’d assume a broken reference degrades gracefully — the feature that uses it fails, the rest of the app carries on. It does not. A single orphaned connection reference — one pointing at a connector that no longer resolves — breaks authentication for the whole app. Not the one screen that needs it. All of it.

So when auth mysteriously stops working after you’ve been adding and removing connectors, don’t go hunting through your React. Go audit your connection references and find the dead one. Prune it and the app comes back to life. The blast radius is total, which is exactly why it’s confusing — the symptom looks nothing like the cause.

5. Patch the header the runtime rejects after every regenerate

When you regenerate connector code — which you do every time the connector changes — the generated output ships with an HTTP header the runtime flat-out rejects. The request goes out, the host bounces it, and your connector call fails for a reason that has nothing to do with your logic.

The patch is small: strip the offending header out of the generated code. The annoying part is that regenerating overwrites your fix, so every regenerate means re-patching. I ended up scripting it so a human never has to remember. If you regenerate by hand, put a sticky note on your monitor, because this one comes back every single time.

Budget for the platform, not the React

Here’s the takeaway. Every one of these is a platform quirk, not a React problem. Your components are fine. Your hooks are fine. The React you already know carries straight over — that part really is as easy as it looks. What you’re actually learning is how the host serves, authenticates, and wires your app, and that’s where the whole budget goes.

So if you’re scoping a Code App, don’t estimate it like a React build with some config on top. Estimate the React as the easy 20% and the platform seams as the real work. Code Apps are worth it — a modern front end, native on the platform, calling connectors from your own code. Just go in knowing the five afternoons above are part of the deal.


Slides: the condensed version is in a short deck — download the PDF.

Which of these would you have found faster — the silent empty array, or the header that comes back every regenerate?


Share this post:

Next Post
Graph's Convert-to-PDF Doesn't Return a PDF