A production-ready React + TypeScript template designed for rapid frontend development. Features containerized deployment via nginx, automated CI/CD pipeline, health checks, and comprehensive test coverage.
- Node.js 20.x or higher
- Yarn 4 (Berry) (enabled via corepack)
- Docker (optional, for containerized development)
- Azure DevOps account (for CI/CD)
- Clone and install dependencies:
cd %MICROSERVICE_NAME%
corepack enable
yarn install- Start the development server:
yarn devThe application starts on http://localhost:5173 (Vite default port).
- Build for production:
yarn buildThe optimized production build is created in the build/ directory.
- Run tests:
# Run tests in watch mode
yarn test
# Run tests with coverage
yarn coverageThis template follows modern React best practices with Vite as the build tool:
%MICROSERVICE_NAME%/
βββ src/ # Application source code
β βββ App.tsx # Main application component
β βββ App.test.tsx # Application tests
β βββ main.tsx # Application entry point
β βββ setupTests.ts # Test configuration
βββ nginx/ # Nginx configuration for production
β βββ nginx.conf # Main nginx configuration
β βββ conf.d/
β βββ website.conf # Site-specific configuration with health endpoints
βββ public/ # Static assets
βββ build/ # Production build output (generated)
βββ Dockerfile # Multi-stage Docker build configuration
βββ azure-pipelines.yml # CI/CD pipeline definition
βββ .dockerignore # Docker build context exclusions
βββ vite.config.ts # Vite build configuration
βββ vitest.config.ts # Vitest test configuration
βββ tsconfig.json # TypeScript configuration
βββ package.json # Project dependencies and scripts
- Modern Stack: React 18 + TypeScript + Vite for fast development
- Test-Ready: Vitest + React Testing Library pre-configured
- Production Optimized: Nginx-based serving with health endpoints
- Container-First: Multi-stage Docker build for minimal production images
docker build -t %PROJECT_ID%/%MICROSERVICE_NAME%:latest .The Dockerfile uses a multi-stage build:
- Build Stage: Uses
node:20-alpineto compile the application with Vite - Runtime Stage: Uses
nginx:1.17.2-alpineto serve static files (~25 MB)
# Standard port mapping
docker run -p 8080:8080 %PROJECT_ID%/%MICROSERVICE_NAME%:latest
# Alternative port (e.g., 3000 on host -> 8080 in container)
docker run -p 3000:8080 %PROJECT_ID%/%MICROSERVICE_NAME%:latest
# Run in detached mode
docker run -d -p 8080:8080 --name %MICROSERVICE_NAME% %PROJECT_ID%/%MICROSERVICE_NAME%:latestAccess the application:
- With port 8080: http://localhost:8080
- With port 3000: http://localhost:3000
- Health check: http://localhost:8080/-/healthz
The Docker container runs nginx on port 8080 and serves the pre-built static files from /usr/static.
The Azure DevOps pipeline (azure-pipelines.yml) automates linting, testing, building, and publishing Docker images.
The pipeline uses reusable templates from the pipeline-templates repository to promote code reuse across projects:
- Docker Build & Push: Uses
common/jobs/build/docker-build-push.yml- a shared template for building and publishing container images to ACR - Node.js-specific steps: Install, lint, test, and build tasks are project-specific
This architecture allows the same Docker publishing logic to be reused for other projects (e.g., .NET, Python, etc.) without duplication.
- Install: Sets up Node.js and installs dependencies with Yarn
- Lint: Runs ESLint to check code quality (non-blocking)
- Test: Runs unit tests with code coverage collection
- Build: Creates optimized production build with Vite
- Docker Build & Push: Uses shared template to build and publish Docker image to ACR
The pipeline is pre-configured to use the "Mia Demo ACR" service connection. If you're using a different ACR or organization, update the service connection:
Edit azure-pipelines.yml:
dockerRegistryServiceConnection: 'Your-ACR-Connection-Name' # Change if needed
imageRepository: 'react-template' # Your desired image name- Navigate to Project Settings > Service connections in Azure DevOps
- Click New service connection > Docker Registry
- Select Azure Container Registry
- Choose your Azure subscription and ACR registry
- Name the connection (update the pipeline YAML with this name)
- Click Save
- Automatic: Triggers on every push to the
masterbranch - Manual: Can be triggered manually from Azure DevOps UI for any branch
The pipeline produces:
- Docker image tagged with
$(Build.BuildId)andlatest - Test results published to Azure DevOps Test Plans
- Code coverage reports viewable in the pipeline run (Cobertura format)
To deploy the Docker image from Azure Container Registry to Kubernetes, you need an ImagePullSecret.
Option A: Service Principal (Recommended for production)
# Create a service principal with pull access
az ad sp create-for-rbac --name myapp-acr-reader --skip-assignment
az acr show --name myregistry.azurecr.io --query id --output tsv
# Grant AcrPull role
az role assignment create \
--assignee <SERVICE_PRINCIPAL_APP_ID> \
--role AcrPull \
--scope <ACR_RESOURCE_ID>Option B: Admin Credentials (Simpler for development)
# Enable admin user on ACR
az acr update --name myregistry --admin-enabled true
# Get credentials
az acr credential show --name myregistryUsing Service Principal:
kubectl create secret docker-registry acr-secret \
--docker-server=myregistry.azurecr.io \
--docker-username=<SERVICE_PRINCIPAL_APP_ID> \
--docker-password=<SERVICE_PRINCIPAL_PASSWORD> \
--namespace=your-namespaceUsing Admin Credentials:
kubectl create secret docker-registry acr-secret \
--docker-server=myregistry.azurecr.io \
--docker-username=myregistry \
--docker-password=<ADMIN_PASSWORD> \
--namespace=your-namespaceapiVersion: apps/v1
kind: Deployment
metadata:
name: react-template
spec:
template:
spec:
imagePullSecrets:
- name: acr-secret
containers:
- name: frontend
image: myregistry.azurecr.io/%PROJECT_ID%/%MICROSERVICE_NAME%:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /-/healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /-/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5| Field | Description | Example |
|---|---|---|
username |
Service principal ID or ACR admin username | 12345678-abcd-1234-abcd-123456789abc |
password |
Service principal secret or ACR admin password | uXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
server |
ACR registry hostname | myregistry.azurecr.io |
# Run tests in watch mode
yarn test
# Run tests once
yarn test run
# Run with coverage
yarn coverageCoverage reports are generated in the coverage/ directory:
# Open HTML coverage report
open coverage/index.html # macOS
xdg-open coverage/index.html # LinuxTests use:
- Vitest: Fast unit test framework compatible with Vite
- React Testing Library: User-centric testing utilities
- @testing-library/jest-dom: Custom matchers for DOM elements
- happy-dom: Lightweight DOM implementation for tests
- Create test file next to component:
MyComponent.test.tsx - Import testing utilities and component
- Write tests using
describeanditblocks - Use
screenqueries to find elements
Example:
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import MyComponent from './MyComponent'
describe('MyComponent', () => {
it('renders the component', () => {
render(<MyComponent />)
expect(screen.getByText('Expected Text')).toBeInTheDocument()
})
})The nginx configuration includes Mia-Platform standard health check endpoints:
Purpose: Kubernetes liveness probe endpoint
Response: 200 OK
{
"status": "ok",
"message": "Service is healthy"
}Purpose: Kubernetes readiness probe endpoint
Response: 200 OK
{
"status": "ok",
"message": "Service is ready"
}Usage:
- Kubernetes
livenessProbeandreadinessProbe - Load balancer health checks
- Monitoring systems
These endpoints are configured in nginx/conf.d/website.conf and return static JSON responses without hitting the application logic.
// src/pages/About.tsx
export default function About() {
return <div>About Page</div>
}
// In src/App.tsx (with React Router)
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import About from './pages/About'
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
)
}// src/services/api.ts
const API_BASE = import.meta.env.VITE_API_BASE_URL || '/api'
export async function fetchData() {
const response = await fetch(`${API_BASE}/data`)
if (!response.ok) throw new Error('Failed to fetch')
return response.json()
}
// Use in component
import { useEffect, useState } from 'react'
import { fetchData } from './services/api'
function MyComponent() {
const [data, setData] = useState(null)
useEffect(() => {
fetchData().then(setData)
}, [])
return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>
}Calling a third-party or customer REST API directly from the browser usually fails with a CORS error while the app is running in a Mia Flow preview session, since that API won't have allow-listed the preview origin.
src/lib/externalProxy.ts works around this by routing the request through the preview container's server-side proxy instead of calling the external API directly from the browser:
import { fetchExternal, isPreviewSandbox } from './lib/externalProxy'
useEffect(() => {
if (!isPreviewSandbox()) return // no proxy outside a Mia Flow preview session
fetchExternal('https://api.example.com', 'v1/items?limit=10')
.then((res) => res.json())
.then(setItems)
}, [])This is a prototyping-only mechanism tied to the Mia Flow preview container - it isn't available (and isn't needed) once the app is built and deployed on its own. In a real deployment, call the API URL directly and make sure it allows your production origin via CORS.
Create .env files for environment-specific configuration:
.env.local (for local development):
VITE_API_BASE_URL=http://localhost:5000
VITE_FEATURE_FLAG=true
Access in code:
const apiUrl = import.meta.env.VITE_API_BASE_URLNote: Only variables prefixed with VITE_ are exposed to the client.
With Context API:
import { createContext, useContext, useState } from 'react'
const AppContext = createContext(null)
export function AppProvider({ children }) {
const [state, setState] = useState({ user: null })
return (
<AppContext.Provider value={{ state, setState }}>
{children}
</AppContext.Provider>
)
}
export function useAppContext() {
return useContext(AppContext)
}| Script | Description |
|---|---|
yarn dev |
Start development server on http://localhost:5173 |
yarn build |
Create optimized production build |
yarn preview |
Preview production build locally |
yarn test |
Run tests in watch mode |
yarn coverage |
Run tests with coverage report |
yarn lint |
Check code quality with ESLint |
- React Documentation
- Vite Documentation
- Vitest Documentation
- React Testing Library
- Docker Best Practices
- Azure Container Registry
- Kubernetes ImagePullSecrets
For detailed guidance on AI-assisted development patterns and best practices, see ai-instructions.md.
This template is provided by Mia-Platform for use in frontend development projects.
Built with β€οΈ for Mia Flow