Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions .github/workflows/build-blazor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,23 @@ name: Build - Blazor
on:
pull_request:
branches: [ "main" ]

workflow_dispatch:

jobs:
build:

runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src/MauiBlazor.UI/MauiBlazor.UI.Web/
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 7.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
89 changes: 47 additions & 42 deletions .github/workflows/build-maui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,60 +11,55 @@ permissions:
contents: read

env:
DOTNET_NOLOGO: true # Disable the .NET logo
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience
DOTNET_CLI_TELEMETRY_OPTOUT: true # Disable sending .NET CLI telemetry
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_VERSION: 10.0.x

jobs:
# MAUI Android Build
build-android:
runs-on: windows-latest
defaults:
run:
working-directory: ./src/MauiBlazor.UI/MauiBlazor.UI.Mobile/
name: Android Build
steps:

- name: Checkout
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources

- name: Restore Dependencies
run: dotnet restore

- name: Build MAUI Android
run: dotnet publish -c Release -f net7.0-android --no-restore
run: dotnet publish -c Release -f net10.0-android --no-restore

- name: Upload Android Artifact
if: ${{ github.event_name == 'push' }}
uses: actions/upload-artifact@v3.1.0
uses: actions/upload-artifact@v4
with:
name: mauiblazor-android-ci-build
path: /a/MauiBlazorTemplate/MauiBlazorTemplate/src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net7.0-android/publish
path: src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net10.0-android/publish

# MAUI Windows Build
build-windows:
runs-on: windows-2022
runs-on: windows-latest
defaults:
run:
working-directory: ./src/MauiBlazor.UI/MauiBlazor.UI.Mobile/
name: Windows Build
steps:

- name: Checkout
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
Expand All @@ -73,31 +68,36 @@ jobs:
run: dotnet restore

- name: Build MAUI Windows
run: dotnet publish -c Release -f net7.0-windows10.0.19041.0 --no-restore
run: dotnet publish -c Release -f net10.0-windows10.0.19041.0 --no-restore

- name: Upload Windows Artifact
if: ${{ github.event_name == 'push' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: mauiblazor-windows-ci-build
path: \a\MauiBlazorTemplate\MauiBlazorTemplate\src\MauiBlazor.UI\MauiBlazor.UI.Mobile\bin\Release\net7.0-windows10.0.19041.0\win10-x64\publish\
path: src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net10.0-windows10.0.19041.0/win10-x64/publish/

# MAUI iOS Build
build-ios:
runs-on: macos-12
runs-on: macos-15
name: iOS Build
# TODO: .NET 10 MAUI workload requires Xcode 26.4 — macos-15 only ships up to 26.3 today.
# Re-enable as a required job once GitHub-hosted runners pick up Xcode 26.4.
continue-on-error: true
defaults:
run:
working-directory: ./src/MauiBlazor.UI/MauiBlazor.UI.Mobile/
steps:
- uses: actions/checkout@v4

- name: Checkout
uses: actions/checkout@v2
- name: Select latest Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
Expand All @@ -106,31 +106,36 @@ jobs:
run: dotnet restore

- name: Build MAUI iOS
run: dotnet build -c Release -f net7.0-ios --no-restore /p:buildForSimulator=True /p:packageApp=True /p:ArchiveOnBuild=False
run: dotnet build -c Release -f net10.0-ios --no-restore /p:buildForSimulator=True /p:packageApp=True /p:ArchiveOnBuild=False

- name: Upload iOS Artifact
if: ${{ github.event_name == 'push' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: mauiblazor-ios-ci-build
path: /Users/runner/work/MauiBlazorTemplate/MauiBlazorTemplate/src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net7.0-ios/iossimulator-x64/*.app
path: src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net10.0-ios/iossimulator-x64/*.app

# MAUI MacCatalyst Build
build-mac:
runs-on: macos-12
runs-on: macos-15
name: MacCatalyst Build
# TODO: .NET 10 MAUI workload requires Xcode 26.4 — macos-15 only ships up to 26.3 today.
# Re-enable as a required job once GitHub-hosted runners pick up Xcode 26.4.
continue-on-error: true
defaults:
run:
working-directory: ./src/MauiBlazor.UI/MauiBlazor.UI.Mobile/
steps:

- name: Checkout
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Select latest Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Setup .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Install MAUI Workload
run: dotnet workload install maui --ignore-failed-sources
Expand All @@ -139,11 +144,11 @@ jobs:
run: dotnet restore

- name: Build MAUI MacCatalyst
run: dotnet publish -f:net7.0-maccatalyst -c:Release --no-restore
run: dotnet publish -f:net10.0-maccatalyst -c:Release --no-restore

- name: Upload MacCatalyst Artifact
if: ${{ github.event_name == 'push' }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: mauiblazor-macos-ci-build
path: /Users/runner/work/MauiBlazorTemplate/MauiBlazorTemplate/src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net7.0-maccatalyst/maccatalyst-x64/publish/
path: src/MauiBlazor.UI/MauiBlazor.UI.Mobile/bin/Release/net10.0-maccatalyst/maccatalyst-x64/publish/
24 changes: 12 additions & 12 deletions .github/workflows/build-webapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Build - WebApi
on:
pull_request:
branches: [ "main" ]
workflow_dispatch:

jobs:
build:
Expand All @@ -12,14 +12,14 @@ jobs:
run:
working-directory: ./src/MauiBlazor.WebApi/
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 7.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
2 changes: 1 addition & 1 deletion .github/workflows/deploy-blazor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
submodules: true

Expand Down
13 changes: 6 additions & 7 deletions .github/workflows/deploy-webapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ jobs:
working-directory: ./src/MauiBlazor.WebApi/

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up .NET
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: '7.0.x'
include-prerelease: true
dotnet-version: '10.0.x'

- name: Build with dotnet
run: dotnet build --configuration Release
Expand All @@ -32,7 +31,7 @@ jobs:
run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp

- name: Upload artifact for deployment job
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: .net-app
path: ${{env.DOTNET_ROOT}}/myapp
Expand All @@ -46,13 +45,13 @@ jobs:

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4.1.7
uses: actions/download-artifact@v4
with:
name: .net-app

- name: Deploy to Azure Web App
id: deploy-to-webapp
uses: azure/webapps-deploy@v2
uses: azure/webapps-deploy@v3
with:
app-name: 'mauiblazortemplate'
slot-name: 'Production'
Expand Down
73 changes: 54 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,63 @@
[![Gitmoji](https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg?style=flat-square)](https://gitmoji.dev)
[![Build and deploy - Blazor](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-blazor.yml/badge.svg?branch=main)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-blazor.yml)
[![Build and deploy - API](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-webapi.yml/badge.svg?branch=main)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-webapi.yml)
[![Build - MAUI apps](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-maui.yml/badge.svg?branch=main)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-maui.yml)
# MAUI + Blazor Solution Template

# Maui + Blazor Solution Template
Template for MAUI Blazor Applications.
[![Build - Blazor](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-blazor.yml/badge.svg)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-blazor.yml)
[![Build - WebApi](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-webapi.yml/badge.svg)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-webapi.yml)
[![Build - MAUI apps](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-maui.yml/badge.svg)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/build-maui.yml)
[![Deploy - Blazor](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-blazor.yml/badge.svg)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-blazor.yml)
[![Deploy - WebApi](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-webapi.yml/badge.svg)](https://github.com/bradystroud/MauiBlazorTemplate/actions/workflows/deploy-webapi.yml)

Contains the basic solution structure needed to build an app that can run on the web, mobile and desktop.
I will continue to build and improve this template, so feedback welcome 😊
![.NET 10](https://img.shields.io/badge/.NET-10-512BD4?logo=dotnet&logoColor=white)
![MAUI](https://img.shields.io/badge/MAUI-Blazor%20Hybrid-512BD4?logo=dotnet&logoColor=white)
[![Gitmoji](https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg)](https://gitmoji.dev)

A pragmatic starting point for an app that runs on **web, mobile, and desktop** from a single Razor component library — backed by an ASP.NET Core Web API.

> 🏆 **A bit of history:** For years there was no official Microsoft template for sharing UI across MAUI and Blazor Web — this repo filled the gap and was the go-to starting point. Microsoft finally caught up in **.NET 9** with the first-party [`dotnet new maui-blazor-web`](https://learn.microsoft.com/en-us/aspnet/core/blazor/hybrid/tutorials/maui-blazor-web-app) template, which is a great choice for greenfield projects. This template still differs in shape (separate ASP.NET Core Web API project + NSwag-generated typed client) and remains useful when you want that split out of the box.

## 🏛 Solution structure

```
📦 MauiBlazor.sln
┣ 📂 src
┣ 📂 MauiBlazor.UI
┣ 📂 MauiBlazor.UI.Core
┣ 📂 MauiBlazor.UI.Mobile
┣ 📂 MauiBlazor.UI.Web
┣ 📂 MauiBlazor.Shared
┣ 📂 MauiBlazor.WebApi
┣ 📂 src
┃ ┣ 📂 MauiBlazor.UI
┃ ┃ ┣ 📂 MauiBlazor.UI.Core # Shared Razor components (RCL)
┃ ┃ ┣ 📂 MauiBlazor.UI.Mobile # MAUI Blazor Hybrid host (Android/iOS/Mac/Windows)
┃ ┃ ┗ 📂 MauiBlazor.UI.Web # Blazor WebAssembly host
┃ ┣ 📂 MauiBlazor.Shared # DTOs shared between API and clients
┃ ┗ 📂 MauiBlazor.WebApi # ASP.NET Core Web API + NSwag client gen
```

## 🚀 Quickstart

```bash
# Prereqs: .NET 10 SDK + MAUI workload
dotnet workload install maui

git clone https://github.com/bradystroud/MauiBlazorTemplate.git
cd MauiBlazorTemplate

# Run the API
dotnet run --project src/MauiBlazor.WebApi

# Run the Blazor web app
dotnet run --project src/MauiBlazor.UI/MauiBlazor.UI.Web

# Run the MAUI app (pick a framework)
dotnet build src/MauiBlazor.UI/MauiBlazor.UI.Mobile -f net10.0-maccatalyst
```

Currently there is no template to automatically kick you off and have a single UI that can be used on a website and a MAUI app.
I hope to save developers a ton of work.
Check out my blog where I dive a bit deeper into the purpose of each project https://medium.com/@bradystroud/maui-blazor-template-84cb6b8db97
## 🧰 Stack

- **.NET 10** — single SDK across all projects
- **MAUI Blazor Hybrid** — native shell, Razor UI
- **Blazor WebAssembly** — same UI in the browser
- **ASP.NET Core Web API** with **NSwag** for OpenAPI + typed client generation
- **GitHub Actions** for CI/CD to Azure Static Web Apps + Azure App Service

## 📖 Background

Read the original write-up: [MAUI Blazor Template on Medium](https://medium.com/@bradystroud/maui-blazor-template-84cb6b8db97).

Feedback and PRs welcome 😊

Brady
— Brady
5 changes: 3 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"sdk": {
"version": "7.0.102"
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
}
Loading
Loading