Skip to content

Building a PowerApps CodeApp with Claude Code Part 3

Abstract Header Image

Introduction

In part three of this series of blog posts, I get Claude Code ready to work with the CodeApp project and try the first couple of prompts.

Getting ready to use Claude Code

At this point, we’ve moved far enough away from the initial empty template project that we can set ourselves up to use Claude Code.

I’ve already got Claude Code installed, and my Anthropic subscription is at the Pro level, so I can use Claude Code — it’s not supported on the free tier anymore, unfortunately. If you want to install the tool, use their quick start guide:

https://code.claude.com/docs/en/quickstart

From a console window at the root of my project, I run the command to start Claude Code:

claude

Claude Code runs in a console window in its own interactive session, so you need a dedicated console window for it. This threw me a bit when I first used it, but it works well, and I can see why it’s built this way. When I’m working with it, I tend to run a Claude Code session in a console window and a Visual Studio Code session with the project open simultaneously.

At this point, I’ve updated Claude Code to the latest version, but I haven’t customised the set-up at all or overridden the default model settings. I want to know what the current ‘standard’ setup is. I’ll experiment more with customising the setup once I’ve familiarised myself with how the stock setup performs.

AI tools live for context, and Claude Code has built-in tools that provide dedicated context to improve the AI’s performance. You can create a ‘CLAUDE.md’ file in the project to provide contextual information to the AI when it’s working, telling it useful information like libraries you want to use, coding standards that should be enforced, and commands that can be used with the project.

Claude Code includes a command to create a new claude.md file by analysing the current project. This is where doing some initial customisation of the project beyond the basic template should help us. From the claude code console, I run:

/init

And it generated a claude.md file for my project. It’s probably worth going through this in some detail since it demonstrates how powerful this tool is, and what the claude.md file is trying to achieve.

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Context
Power Apps Code App ("Map Codeapp") built with Vite, React 19, TypeScript, and Tailwind CSS v4. It runs both as a normal web app during development and as a Power Platform Code App in production, reading data from two Dataverse tables via Power Platform connections: `Location` (`ddm_location`) and `Point of Interest` (`ddm_pointofinterest`).
Prioritize readability over cleverness. Ask clarifying questions before making architectural changes.
The app is currently at the default Vite+React scaffold stage (`src/App.tsx`, `src/main.tsx`) - the Dataverse-backed map functionality has not been built yet; only the generated data layer exists so far.

From the first section, we can see that Claude Code has already figured out a lot about our project. It lists the libraries we’ve installed, the Dataverse tables we have models and services for, and it also notes that we haven’t customised the default React + Vite template app page yet.

## Commands

```bash
npm run dev # start Vite dev server
npm run build # tsc -b && vite build -> outputs to ./dist (buildPath in power.config.json)
npm run lint # eslint .
npm run preview # preview production build locally
pa app run # run as a Power Platform Code App (Power Platform CLI)
pa app push # push/deploy the app to the Power Platform environment
```
There is no test runner configured in this repo.

In the next section, Claude Code has outlined the commands we can use for our project, including those for the Power App CLI tools. It lists both the standard npm command to start the dev server and the Power Apps CLI command to do the same, so we might want to fix that later if it causes any problems.

Claude Code also points out that we currently have no test runner configured for the project. That might be something to address later, but not something I want to spend time on now.

## Architecture

### Power Apps Code App integration
- `power.config.json` is the Code App manifest: app id, target environment (`environmentId`, `region`), build output settings, and `databaseReferences`, which map each Dataverse table (`ddm_location`, `ddm_pointofinterest`) to a local data source name (`locations`, `pointsofinterest`).
- `vite.config.ts` wires in the `powerApps()` plugin from `@microsoft/power-apps-vite` alongside `@vitejs/plugin-react` and `@tailwindcss/vite`. This plugin is what generates the `.power/` and `src/generated/` trees from the configured Dataverse tables.
- `.power/schemas/dataverse/*.Schema.json` are the raw per-table Dataverse schemas; `.power/schemas/appschemas/dataSourcesInfo.ts` is the generated data-source registry consumed by the generated services. Everything under `.power/` is generated - don't hand-edit it.
### Generated data layer (`src/generated/`)
**Autogenerated by the power-apps-vite plugin - never edit files here directly; they regenerate from the Dataverse schema.**
- `src/generated/models/<Table>Model.ts` - TypeScript types per table: a `<Table>Base` interface (the table's own fields, including option-set fields typed as string-literal unions backed by a const lookup object, e.g. `Ddm_locationsstatecode`) and a `<Table>` interface that extends `Base` with Dataverse system/lookup metadata (`createdby`, `ownerid`, `_x_value` lookup fields, etc.).
- `src/generated/services/<Table>Service.ts` - a static-method service class per table (`create`, `update`, `delete`, `get`, `getAll`, `getMetadata`), each built on a shared `getClient(dataSourcesInfo)` client from `@microsoft/power-apps/data`. `create`/`update` take the `Base` type with the primary key omitted/partial; reads return the full type with metadata.
- `src/generated/index.ts` re-exports all generated models and services - import from here (`from './generated'`) rather than reaching into individual model/service files.
When adding app features that read/write Dataverse data, go through these generated services rather than calling `@microsoft/power-apps/data` directly.
### App shell
- `src/main.tsx` mounts `<App />` from `src/App.tsx` into `#root` (see `index.html`) inside `React.StrictMode`.
- Tailwind v4 is applied via the `@tailwindcss/vite` plugin (no separate `tailwind.config.js` needed) - global styles live in `src/index.css`.

The last section of the ‘CLAUDE.md’ file looks at the architecture of the app so far and is an interesting read for me, since it examines the auto-generated files in more detail than I have. It’s also noted that those files are auto-generated and shouldn’t be edited directly, which is perfect.

We don’t edit the auto-generated code because changes may be overwritten when the code is regenerated or after an update, and they may be lost.

Putting Claude Code to work

I create an empty ‘components’ folder under the ‘src’ folder in our project, then create an empty ‘azure-map.tsx’ file in the ‘components’ folder.

While I was searching for instructions for Azure Maps, one of the search results was AI generated and gave some incomplete looking code to get working with the API. I dump this into the ‘azure-map.tsx’ file.

import React from 'react';
import { AzureMap, AzureMapsProvider, IAzureMapOptions } from 'react-azure-maps';
import { AuthenticationType } from 'azure-maps-control';

const mapOptions: IAzureMapOptions = {
authOptions: {
authType: AuthenticationType.subscriptionKey,
subscriptionKey: '<Your Azure Maps Key>'
},
center: [-122.33, 47.6],
zoom: 10,
language: 'en-US'
};

This lights up in the editor as all kinds of wrong, and I notice it’s also missing the CSS declaration that has been given as a separate lone line:

import 'azure-maps-control/dist/atlas.min.css';

I add that to the declarations at the top.

Now comes the magic trick. With everything we’ve set up, we’re ready to use Claude Code to turn that into something useful. Our first prompt in our Claude Code console session is going to be:

complete the azure-map.tsx component so that it's a re-usable react map component that uses the azure maps react components

So that’s not the most complex prompt, but it’s got enough information to give Claude Code a task and a scope to work within, and the ‘claude.md’ file should be providing additional context. If this were a more serious task where I was worried about the outcome, I could get Claude Code to generate a plan first, which I could check through and correct if necessary, but I don’t think I need that for this step, and I’d like to try and get something working quickly.

I don’t have auto-accept turned on, so I watch as updates pop up one after another in the console, reviewing each one and accepting it. The component gets completed, but the agent also takes some other steps, like creating a ‘.env.example’ file where our primary API key for Azure Maps can live.

Towards the end of the run, Claude Code runs a linting step, detects errors in the newly built component, and then performs a second iteration to fix them.

There are also instructions for creating the local ‘.env.local’ file and adding our primary API key to it. It is important to make sure local files like this are excluded from source control to make sure secrets don’t end up in the repo.

At the end of an agent session like that, Claude Code will give you a recap of the steps taken, in case you were using auto-accept and want the headline version of what happened. It can also be useful to review in case you missed anything in the sizeable code changes that have come through.

I do a quick build to confirm Claude Code’s assertion that everything is clean and building ok, and it is.

I take a moment to create the ‘.env.local’ file now, as suggested, and grab my Primary API key from my Azure Maps account. You can find the shareable API keys under Settings — Authentication on the Azure portal page for the Azure Maps account.

So now that we have a reusable map component, let’s give Claude Code a prompt to replace the Vite + React template with a header, a sidebar and a main panel displaying the map component.

I’m going to stick to using simpler prompts for now, in the hope of getting something up and running quickly. I can ask Claude to revise things later if I don’t like what I get. Next prompt:

Remove the vite + react template and replace it with a react app framework with a header and a sidebar, with the azure-map component displayed in the main panel.

Again, I watch as the agent takes steps to do what I asked, reviewing each step and accepting them one after another. This time, more steps are taken, such as creating components for a Header and a Sidebar in a sub-folder named ‘layout’ within components. The template contents of the main ‘app.tsx’ file are stripped out and replaced with new content using the newly created components, with some basic layout around it styled with Tailwind commands. It’s also updated the default ‘index.html’ file to remove the last of the Vite branding and put in the app name.

As before, it finishes up with a linting step and a build to make sure everything is still working, and gives me a summary of the steps taken.

This is a good point to run the app and see what I’ve got. I do my own build again to confirm what Claude is telling me, and it builds fine. To start the local server I use:

pa app run

The app pops up, looking pretty good, but the actual map area is blank.

At this point, I realise I don’t have the React Dev Tools installed in my browser for some reason, so I rectify that quickly and poke around for a bit in the browser and the project. I can see some error messages that seem to be talking about authentication with the Azure Maps service, and after looking over the code that’s loading the API key from the environment file and seeing that everything looks good, I check the API key in the ‘.env.local’ file and realise I copy/pasted it wrong, missing out the first few characters.

I fix that, refresh everything and up pops my map, centred on San Francisco, as I didn’t change the default location. A bit more messing around to find some coordinates for Oxford and change the default location, and my app now looks like this:

That looks pretty good, mainly because the Azure Maps React components look good, but it’s pretty much the layout I envisaged when I had this idea, and I haven’t had to write any code yet.

Our ‘CLAUDE.md’ file is now out of date. We’ve replaced the Vite + React template code with our new layout and Azure Maps control. Let’s tell Claude to update the ‘claude.md’ file so it’s up to date with the latest version of the project:

revise the CLAUDE.md file so that it's up to date with the latest project changes

This only generates a couple of updates, but it does exactly what I wanted: it replaces the text about the Vite + React template still being there with text describing how the app is laid out now. It also includes a note about how the Azure Maps API key is stored and updates the App Shell section to describe the new structure.

This describes a basic developer loop you can use with Claude Code. Give it a basic context to work with, have it make changes to your code, then have it update the context and then do the next iteration, getting it to make more changes.

What’s Next

We have a development loop running now, so in the next iteration it’s time to build some real features. I’ll pull in some data from Dataverse and display it in the side panel and see if I can get some points of interest appearing on the map.

If you want to learn more about the Microsoft Team here at Capgemini, take a look at our open roles and consider joining the team!


Building a PowerApps CodeApp with Claude Code Part 3 was originally published in Capgemini Microsoft Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Kieran Holmes originally posted this article on 28 October 2026 at 9:35 AM.

Leave a Reply