Back to Blog
#Power Apps#React#TypeScript#Low-Code#Microsoft#Enterprise

Power Apps Code Apps: Modern Web Development Meets Low-Code Platform

Power Apps Code Apps: Modern Web Development Meets Low-Code Platform

As developers, we've long faced a choice: build custom web applications with full control but handle authentication, data access, and deployment ourselves, or use low-code platforms that provide infrastructure but limit our technical freedom. Microsoft's Power Apps Code Apps eliminates this trade-off.

The Gap in the Power Platform Ecosystem

Power Platform has two primary application types:

  • Canvas Apps: Visual drag-and-drop for citizen developers, great for rapid development but limited by Power Fx expressions
  • Model-Driven Apps: Data-driven forms perfect for Dataverse-centric apps, but UI customization is constrained

If you're a professional web developer who needs:

  • Full control over UI/UX design
  • Modern JavaScript frameworks (React, Vue, Angular)
  • Local development with hot reload
  • Standard Git workflows and tooling
  • Type-safe code with IntelliSense

...you've historically had to build everything from scratch outside the Power Platform.

Code Apps changes this. It's a code-first approach that lets you build React applications in VS Code while automatically getting:

  • Enterprise authentication via Microsoft Entra ID
  • Access to 1,500+ Power Platform connectors
  • Auto-generated TypeScript interfaces from Dataverse
  • Simplified deployment and hosting
  • Automatic governance (DLP, Conditional Access, sharing policies)

Who Should Use Code Apps?

For React Developers New to Power Platform

If you're a React developer, think of Code Apps as:

"React + TypeScript + an enterprise backend-as-a-service that's already approved by your IT department"

You get to:

  • Write standard React components with hooks
  • Use your favorite npm packages
  • Debug locally in VS Code
  • Deploy with a single CLI command (pac code push)

What you gain: Instant access to SharePoint, SQL Server, Office 365, Dataverse, and 1,500+ other connectors without writing backend APIs or managing authentication.

What you give up: Mobile app support (web only in preview), and you're tied to Power Platform hosting.

For Power Platform Developers Leveling Up

If you're familiar with Canvas or Model-Driven apps, Code Apps represents:

"The full power of JavaScript and modern web frameworks, while staying in the Power Platform ecosystem"

You can now:

  • Implement complex custom UI/UX that Power Fx can't handle
  • Use TypeScript for better code organization and safety
  • Leverage React component libraries like Fluent UI
  • Build performant, scalable applications with modern patterns

When to switch: Your Canvas app has hit complexity limits, needs specific performance optimizations, or requires UI components that don't exist in Power Apps Studio.

When to stay with Canvas: Rapid prototyping, citizen developer scenarios, or offline mobile app requirements.

Technical Architecture

Code Apps sits on a modern web stack that will feel instantly familiar to React developers:

┌─────────────────────────────────────────┐
│     Your React Application Code        │
├─────────────────────────────────────────┤
│   @microsoft/power-apps SDK             │
│   - initialize()                        │
│   - getContext()                        │
├─────────────────────────────────────────┤
│   Auto-Generated Type-Safe Layer        │
│   - SystemusersService.ts               │
│   - SystemusersModel.ts (interfaces)    │
├─────────────────────────────────────────┤
│   power.config.json                     │
│   - Data source definitions             │
│   - Environment configuration           │
├─────────────────────────────────────────┤
│   Power Apps Host Runtime               │
│   - Authentication                      │
│   - Connector invocation                │
│   - Policy enforcement                  │
└─────────────────────────────────────────┘

Key Components:

  • React 18.2.0: Specifically required version (use --save-exact)
  • Vite: Modern build tool with hot module replacement
  • TypeScript: Full type safety with generated interfaces
  • Power Apps SDK: @microsoft/power-apps npm package
  • Power Platform CLI: pac command for data sources and deployment

Getting Started: 5 Minutes to Your First Code App

Prerequisites

# Install Power Platform CLI (if not already installed)
dotnet tool install --global Microsoft.PowerApps.CLI.Tool

# Verify installation
pac

Create Your First App

# 1. Create React + TypeScript project with Vite
npm create vite@latest my-code-app -- --template react-ts
cd my-code-app

# 2. Install Power Apps SDK and dependencies
npm install @microsoft/power-apps react@18.2.0 react-dom@18.2.0 --save-exact
npm install @fluentui/react-components @fluentui/react-icons

# 3. Initialize as Code App
pac auth create --environment <YOUR_ENVIRONMENT_ID>
pac code init --displayName "My Code App" --description "My first code app"

# 4. Add Dataverse data source
pac code add-data-source -a dataverse -t contact

Critical Configuration: vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  base: "./",
  server: {
    host: "::",
    port: 3000,  // Required by Power SDK
  },
  plugins: [react()],
})

Update Package Scripts

{
  "scripts": {
    "dev": "start /B vite && pac code run",
    "build": "tsc -b && vite build"
  }
}

The Magic: Auto-Generated Type-Safe Code

When you run pac code add-data-source, the CLI generates TypeScript interfaces and service methods automatically. Here's what you get:

Generated Model Interface (src/generated/models/SystemusersModel.ts)

export interface Systemusers {
  systemuserid: string;
  fullname?: string;
  internalemailaddress: string;
  isdisabled?: Systemusersisdisabled;
  domainname: string;
  title?: string;
  // ... 100+ additional strongly-typed properties
}

export const Systemusersisdisabled = {
  0: 'Enabled',
  1: 'Disabled'
} as const;
export type Systemusersisdisabled = keyof typeof Systemusersisdisabled;

Generated Service Layer (src/generated/services/SystemusersService.ts)

export class SystemusersService {
  public static async create(
    record: Omit<Systemusers, 'systemuserid'>
  ): Promise<IOperationResult<Systemusers>> {
    // Auto-generated CRUD operation
  }

  public static async getAll(
    options?: IGetAllOptions
  ): Promise<IOperationResult<Systemusers[]>> {
    const result = await this.client.retrieveMultipleRecordsAsync<Systemusers>(
      this.dataSourceName, 
      options
    );
    return result;
  }

  // update(), delete(), get() also generated
}

What this gives you:

  • IntelliSense autocomplete for all table columns
  • Compile-time type checking
  • No manual API calls or endpoint configuration
  • OData query support ($select, $filter, $expand, $orderby)

Building a Real Application: Practical Patterns

Pattern 1: SDK Initialization (Critical!)

The Power Apps SDK must be initialized before any data operations. This is the #1 mistake new developers make.

WRONG - Will fail silently or throw errors:

function App() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    loadUsers(); // SDK not ready!
  }, []);
}

CORRECT - Two-stage initialization:

import { initialize } from '@microsoft/power-apps';
import { SystemusersService } from './generated/services/SystemusersService';

function App() {
  const [isInitialized, setIsInitialized] = useState(false);
  const [users, setUsers] = useState<Systemusers[]>([]);

  // Stage 1: Initialize SDK
  useEffect(() => {
    const init = async () => {
      try {
        await initialize();
        setIsInitialized(true);
      } catch (err) {
        console.error('SDK initialization failed:', err);
      }
    };
    init();
  }, []);

  // Stage 2: Load data ONLY after initialization
  useEffect(() => {
    if (!isInitialized) return;
    
    const loadUsers = async () => {
      const result = await SystemusersService.getAll({
        select: ['systemuserid', 'fullname', 'internalemailaddress'],
        filter: "isdisabled eq false",
        orderBy: ['fullname asc'],
        top: 100,
      });

      if (result.success && result.data) {
        setUsers(Object.values(result.data));
      }
    };

    loadUsers();
  }, [isInitialized]);

  if (!isInitialized) {
    return <Spinner label="Initializing..." />;
  }

  return <UserList users={users} />;
}

Pattern 2: Type-Safe CRUD Operations

// CREATE
const newUser = {
  firstname: "Jane",
  lastname: "Developer",
  internalemailaddress: "jane@example.com"
};
const createResult = await SystemusersService.create(newUser);

// READ with OData queries
const readResult = await SystemusersService.get(userId, {
  select: ['fullname', 'jobtitle'],
  expand: { 
    businessunitid: { select: ['name'] } 
  }
});

// UPDATE (partial)
await SystemusersService.update(userId, {
  jobtitle: "Senior Developer"
});

// DELETE
await SystemusersService.delete(userId);

// QUERY with filters
const queryResult = await SystemusersService.getAll({
  filter: "isdisabled eq false and contains(fullname, 'Smith')",
  orderBy: ['createdon desc'],
  top: 50
});

Pattern 3: Error Handling

const loadData = async () => {
  try {
    setLoading(true);
    setError(null);
    
    const result = await SystemusersService.getAll({ top: 100 });

    if (!result.success) {
      throw new Error('Failed to retrieve data');
    }

    setUsers(Object.values(result.data));
  } catch (err) {
    setError(err instanceof Error ? err.message : 'Unknown error');
    console.error('Data load error:', err);
  } finally {
    setLoading(false);
  }
};

Pattern 4: Fluent UI Integration

import { 
  DataGrid, 
  DataGridBody, 
  DataGridRow, 
  DataGridCell,
  TableColumnDefinition,
  createTableColumn
} from '@fluentui/react-components';

const columns: TableColumnDefinition<Systemusers>[] = [
  createTableColumn<Systemusers>({
    columnId: 'fullname',
    renderHeaderCell: () => 'Full Name',
    renderCell: (item) => item.fullname,
  }),
  createTableColumn<Systemusers>({
    columnId: 'status',
    renderHeaderCell: () => 'Status',
    renderCell: (item) => (
      <Badge 
        color={item.isdisabled ? "danger" : "success"} 
        appearance="filled"
      >
        {item.isdisabled ? 'Disabled' : 'Enabled'}
      </Badge>
    ),
  }),
];

export function UserGrid({ items }: { items: Systemusers[] }) {
  return (
    <DataGrid items={items} columns={columns} sortable resizableColumns>
      <DataGridBody>
        {({ item }) => (
          <DataGridRow key={item.systemuserid}>
            {({ renderCell }) => <DataGridCell>{renderCell(item)}</DataGridCell>}
          </DataGridRow>
        )}
      </DataGridBody>
    </DataGrid>
  );
}

Code Apps vs Canvas Apps vs Model-Driven Apps

Decision Matrix

ScenarioRecommended Choice
Professional dev team, complex custom UICode Apps
Rapid prototyping, citizen developersCanvas Apps
Data-driven forms, standard UI acceptableModel-Driven Apps
Offline mobile app requiredCanvas Apps
Need specific JavaScript librariesCode Apps
Complex business logic, custom algorithmsCode Apps
Standard CRUD over DataverseModel-Driven Apps
Integration-heavy with external APIsCode Apps

Feature Comparison

FeatureCode AppsCanvas AppsModel-Driven Apps
UI FlexibilityFull controlHighLimited
Developer SkillProfessionalCitizen developerBusiness user
Development SpeedModerateFastVery fast
Local Development✅ Yes❌ No❌ No
Git Workflows✅ Standard⚠️ Limited⚠️ Limited
Framework ChoiceReact/Vue/AngularN/AN/A
Mobile App❌ Web only (preview)✅ Native✅ Native
Offline Support❌ No✅ Yes✅ Yes
Data Sources1,500+ connectors1,500+ connectorsDataverse only
TypeScript Support✅ Full❌ No❌ No

Critical Gotchas and Best Practices

1. React Version Constraint

# MUST use exactly React 18.2.0
npm install react@18.2.0 react-dom@18.2.0 --save-exact

2. Port Configuration

Port 3000 is required and must match in both files:

power.config.json:

{
  "localAppUrl": "http://localhost:3000/"
}

vite.config.ts:

{
  server: {
    port: 3000
  }
}

3. Never Manually Edit Generated Files

src/generated/  <- DO NOT EDIT

These files are regenerated when data sources change. Put custom logic in your application code, not generated services.

4. Always Build Before Push

npm run build      # Compiles to /dist
pac code push      # Deploys /dist to Power Platform

Forgetting npm run build will deploy outdated code.

5. Browser Profile Matters

Open the app URL in the same browser profile as your Power Platform tenant. Authentication won't work across different profiles.

Deployment and ALM

Local Development

npm run dev  # Starts Vite dev server + Power SDK

Access at http://localhost:3000

Publish to Power Platform

npm run build
pac code push

Add to Solution for ALM

pac solution add-reference --path . --solution MySolution

Now your Code App can be exported, versioned, and deployed through Power Platform solutions like any other component.

Current Limitations (Preview Status)

Code Apps is currently in preview, which means:

Not Yet Supported:

  • Content Security Policy (CSP)
  • Power Apps mobile app
  • Power Platform Git integration
  • Native Application Insights integration
  • SharePoint forms embedding
  • Power BI data integration

⚠️ Production Readiness: Microsoft recommends avoiding preview features for production workloads. Evaluate carefully based on your organization's risk tolerance.

Real-World Use Cases

When Code Apps Shines

  1. Custom Dashboards with Complex Visualizations

    • Use D3.js or Chart.js for advanced charts
    • Pull data from Dataverse + SQL + SharePoint
    • Full control over responsive layout
  2. Form-Heavy Applications with Conditional Logic

    • React Hook Form for complex validation
    • Multi-step wizards with state preservation
    • Dynamic form generation based on user roles
  3. Integration-Heavy Line-of-Business Apps

    • Combine data from 10+ connectors
    • Custom business logic in TypeScript
    • Optimized performance with React memoization
  4. Migrating Legacy Internal Tools

    • Modernize old ASP.NET apps
    • Keep enterprise auth and data access
    • Simplified deployment to Power Platform

Sample Application: System Users Dashboard

A sample repository demonstrates a complete implementation:

Features Implemented:

  • SDK initialization with proper error handling
  • Dataverse integration (systemusers table)
  • Fluent UI DataGrid with sorting and resizing
  • Status badges with conditional formatting
  • TypeScript throughout with full type safety

Key Files to Study:

  • src/App.tsx - Main application logic and initialization pattern
  • src/generated/services/SystemusersService.ts - Auto-generated CRUD service
  • src/generated/models/SystemusersModel.ts - Type-safe interfaces
  • power.config.json - Data source configuration
  • vite.config.ts - Build and dev server setup

Getting Help and Resources

Official Documentation

Key Tools

Conclusion

Power Apps Code Apps represents a significant evolution in enterprise application development. It bridges two worlds that have traditionally been separate:

  • For React developers: A fast path to enterprise data, authentication, and deployment without building backend infrastructure
  • For Power Platform teams: The ability to tackle complex requirements that exceed Canvas/Model-Driven app capabilities while staying in the governed ecosystem

The developer experience is modern and familiar—you're writing React with TypeScript in VS Code, using Git, and debugging locally. But you're getting instant access to enterprise connectors, automatic type generation, and simplified deployment that would normally take weeks to set up manually.

Should you use it today? If you're experimenting, learning, or building internal tools where preview status is acceptable, absolutely. The productivity gains from auto-generated TypeScript interfaces and instant connector access are substantial.

For production applications, weigh the preview limitations (no mobile support, limited Git integration) against your requirements. Many organizations are successfully using preview features with appropriate governance and monitoring.

The future direction is clear: Microsoft is investing in bringing professional developers into the Power Platform ecosystem. Code Apps is the entry point for React developers to discover how low-code platforms can accelerate enterprise development without sacrificing technical control.

Ready to try it? Clone the sample repository, run through the quickstart, and see how quickly you can build a type-safe, connector-enabled React app with enterprise authentication.


Thanks for reading! If you enjoyed this post, feel free to share it.

Read more posts