An Angular code app is the one stack that is not a drop-in for Power Apps, so it deserves a careful guide. A Power Apps code app is just a Vite app, and most frameworks add a single plugin to a Vite config you own. Angular is different, however, because its CLI hides the Vite config you would normally edit. As a result, you must bring Vite back yourself before the Power Apps plugin can slot in.
This guide follows a build we actually tested. Moreover, it flags the exact misconfiguration that silently breaks the bundle, so you can avoid it.
Why an Angular code app needs a workaround
The other stacks are npm create vite templates, so you own a vite.config.ts and simply add the powerApps() plugin. Angular’s first-party tooling does not expose that config, because ng build wraps Vite and esbuild internally. Therefore there is no plugins array to extend.
To make an Angular code app, you run Angular on a Vite config you control. Specifically, you use the AnalogJS vite-plugin-angular. Once Angular runs on your own Vite config, the same framework-agnostic glue applies: the @microsoft/power-apps SDK and CLI, the @microsoft/power-apps-vite plugin, and a power.config.json file.
Build your Angular code app step by step
First, assemble a small Vite project by hand, since no create-vite Angular template exists. Six files are enough. Start with package.json: Angular 22 needs TypeScript 6.0 (the peer range is >=6.0 <6.1, so npm install fails with TypeScript 5), and the AnalogJS plugin needs @angular/build as a peer:
{
"name": "my-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" },
"dependencies": {
"@angular/common": "^22.2.3",
"@angular/compiler": "^22.2.3",
"@angular/core": "^22.2.3",
"@angular/platform-browser": "^22.2.3",
"@angular/router": "^22.2.3",
"@microsoft/power-apps": "^1.5.0",
"rxjs": "^7.8.2"
},
"devDependencies": {
"@analogjs/vite-plugin-angular": "^2.9.2",
"@angular/build": "^22.2.3",
"@angular/compiler-cli": "^22.2.3",
"@microsoft/power-apps-vite": "^1.0.13",
"typescript": "~6.0.0",
"vite": "^8.3.5"
}
}

Your vite.config.ts lists the Angular plugin first:
import { defineConfig } from 'vite'
import angular from '@analogjs/vite-plugin-angular'
import { powerApps } from '@microsoft/power-apps-vite/plugin'
export default defineConfig({
resolve: { mainFields: ['module'] },
plugins: [angular(), powerApps()],
})
Then add the entry point and a minimal standalone component. Angular 22 is zoneless by default, so do not import zone.js: Vite fails the build with “Rolldown failed to resolve import zone.js” if you do.
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Angular App</title>
</head>
<body>
<app-root></app-root>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser'
import { AppComponent } from './app.component'
bootstrapApplication(AppComponent).catch((err) => console.error(err))
// src/app.component.ts
import { Component } from '@angular/core'
@Component({
selector: 'app-root',
standalone: true,
template: `<h1>Hello from an Angular code app</h1>`,
})
export class AppComponent {}
Finally, the two tsconfig files. The second one is the gotcha explained below: the plugin reads tsconfig.app.json and follows files from src/main.ts:
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"experimentalDecorators": true,
"useDefineForClassFields": false,
"skipLibCheck": true
},
"files": ["src/main.ts"]
}
// tsconfig.app.json
{
"extends": "./tsconfig.json",
"files": ["src/main.ts"]
}

Next, install and build:
npm install
npm run build

Then install the Power Apps CLI once (it provides the pa command), bind the app to your environment, and publish. The init output hints at running npm install again; that installs the project-local copy of the CLI and is optional, because the global pa command already works:
npm install --global @microsoft/power-apps-cli
pa app init -n "My Angular App" -e <environment-id>
pa app run
npm run build
pa app push

When pa app run starts the dev server, the plugin prints a Local Play URL. Open it in the same browser profile as your Power Platform tenant, and accept the browser’s Local Network Access prompt the first time: Chrome and Edge block calls from the player to localhost until you do. When you push, the CLI returns a live app URL.



This is what you should see when you open the live app URL: the app running inside Power Apps after pa app push, with the name you gave pa app init in the header.

The tsconfig gotcha that cost the most time
One detail matters more than any other. The plugin reads tsconfig.app.json, not tsconfig.json, and it uses the files it resolves as the Angular compiler’s program. Consequently, two mistakes break the build quietly.
First, if tsconfig.app.json is missing, the build warns that your component is “not in the TypeScript program.” Second, and worse, a tsconfig.app.json that sets noEmit: true with a glob include produces an empty bundle under one kilobyte, even though the build reports success. Therefore you should use files: ["src/main.ts"] so the program follows imports, and you should not set noEmit.
What to check as you go
Verification is simple once you know the trap. After building, your dist/assets/*.js bundle should weigh roughly 95 KB, not under one kilobyte. Indeed, our Angular code app build reached a healthy bundle of about that size with no plugin warnings, and dist/index.html referenced ./assets/... with relative paths.
Angular-specific notes
To read context, import getContext from @microsoft/power-apps/app in a service and expose it through a signal. Data connectors, meanwhile, generate plain TypeScript services under src/generated/; you inject a thin wrapper and call XxxService.getAll(). Above all, treat Angular as the teaching example of the platform’s boundary: when a framework hides Vite, you bring Vite back.
Stress-test notes
Before publishing, we did more than build this Angular code app – we tested it the way a real project runs. We added the extras a normal app needs: page navigation and a form. We also loaded data in the background and called the Power Apps SDK. Then we opened the running app in an automated browser, a tool called Playwright. Routing, the form, and the data load all worked.
A few things are worth knowing, even if you are new to code.
Don’t make the app wait for Power Apps before it shows anything. The SDK call that reads the signed-in user (getContext()) only finishes when the app runs inside Power Apps. On your laptop it never finishes, so let that call run in the background instead of holding up the first screen.
Turn on the hash version of routing with provideRouter(routes, withHashLocation()). In plain terms, it puts a # in the address bar. That keeps links and the refresh button working inside Power Apps.
Two smaller notes. Forms work out of the box, even with Angular’s newer “zoneless” setup, so you need no extra packages. We also added one tiny line of HTML for a favicon, purely to silence a harmless browser warning. With those settings, the Angular code app ran exactly like the others.
Where to go next
This Angular walkthrough is one stack in a larger set. For the big picture, read the pillar guide on choosing any Vite framework for Power Apps code apps. From there you can compare the smoother stacks, namely React, Vue, Svelte, Solid, and Preact.
For the authoritative platform reference, see the Power Apps code apps documentation on Microsoft Learn.

Leave a Reply