Blazor Hybrid Migration Guide

Updated: 2026-08-08 · Audited: 2026-06-26

Historical planning record — the migration it plans is substantially executed. This guide's tier plan has been overtaken by the work itself: ~55 Razor pages now exist under ALPAMobile.Presentation/Components/Pages/, including LoginPage and the full Jumpseat suite — pages this guide files under Tier 4 ("Defer Late") and Tier 5 ("Keep Native permanently"). Those tier assignments are superseded. The live state of the UI is the ALPAMobile.Presentation project itself; read this document for the migration rationale and per-page traps, not for what remains to migrate.
Direction resolved 2026-06-08: ALPA Mobile is moving to Blazor Hybrid. This guide is the canonical reference for migrating the existing MAUI/XAML pages to Razor components. See also migration-estimate.html for LOE and the straggler-migration pattern overview.

Summary

TierLabelPagesEffort
1Migrate Now16~6–8 weeks
2Migrate Soon16 + 1 control~6–8 weeks
3Defer17 pages/controls~8–10 weeks
4Defer Late54–6 weeks
5Keep Native5N/A

Total estimated effort: 7–10 person-months. See estimate doc for full LOE breakdown, risk factors, and key migration traps.

Prerequisites (P1–P5)

Before any page migration can begin, five infrastructure layers must be in place:

PriorityPrerequisiteWhat it unlocks
P1BlazorWebView integration in AppShell — register services, wire RootComponentTier 1 pages (shell navigation to Razor)
P2CSS design system — Figma token variables, responsive layout, light/darkConsistent visual output across all tiers
P3Service abstraction — INavigationService, IDialogService, ILauncherServiceAll page migration; removes MAUI-only API calls from Razor
P4State management — AppStateContainer or equivalent replacing WeakReferenceMessengerTiers 2–4 where cross-page state flows
P5Navigation dual-routing — Shell route → Blazor component mappingDeep-link compatibility during straggler period

Tier 1 — Migrate Now 16 pages

Low-complexity pages with minimal native coupling. Good candidates to prove out the infrastructure and build team familiarity.

#PageComplexityEffortKey work
1.1AboutPageEASY1–2 daysStatic HTML content; version/build info from injected service
1.2HelpPageEASY1–2 daysStatic content; external links via ILauncherService
1.3FAQPageEASY1–2 daysAccordion FAQ list; <details>/<summary> HTML
1.4InCaseOfAccidentPageEASY1–2 daysStatic steps, phone number tap via ILauncherService
1.5OrangeCardPageEASY1 dayTwo-sided card carousel; phone launch
1.6FTDTUnAugmentedPageLOW2–3 daysCalculator input form; result display grid
1.7FTDTAugmentedPageLOW2–3 daysSame as 1.6 with augmentation toggle
1.8NewPageEASY1 daySimple XAML shell
1.9PilotGroupCommitteePageLOW2–3 daysList + detail via Razor component params
1.10PilotGroupCommitteeDetailsPageLOW1–2 daysRead-only detail view
1.11PilotGroupRepresentativesPageLOW2–3 daysDirectory with search filter
1.12JumpseatInfoPageLOW1–2 daysInfo/links card list
1.13JumpseatEtiquettePageLOW1–2 daysEmbedded video + text
1.14JumpseatMissionPageEASY1 dayStatic text page
1.15JumpseatFlightFinderFrequentSearchesPageLOW2–3 daysEditable list; CRUD via service
1.16MainPageLOW2–3 daysEntry host page; navigates to sub-pages

Tier 2 — Migrate Soon 16 pages + 1 control

Medium complexity. Native coupling present but well-understood. Prerequisite services (P3/P4) must be in place.

#PageComplexityEffortKey work
2.1ContactsPageMEDIUM2–3 daysGrouped list by category; search
2.2UserProfilePageMEDIUM2–3 daysProfile display; quick-action links
2.3MembershipCardPageMEDIUM2–3 daysTwo-sided card; device-idiom layout
2.4OrangeCardPage (dynamic)MEDIUM1–2 daysDynamic content version of Tier 1.5
2.5PushNotificationsSettingsPageMEDIUM3–4 daysToggle subscriptions; clipboard; connectivity
2.6MemberResourcesPageMEDIUM2–3 daysDynamic resource list; external URLs
2.7KCMAirportDetailsPageMEDIUM2–3 daysDetail view; connectivity check
2.8FTDTOperationTypeSelectionPageMEDIUM2–3 daysOperation type picker → navigation
2.9FTDTSplitDutyPeriodPageMEDIUM3–4 daysSplit duty form; time calculations
2.10PilotGroupEventsPageMEDIUM2–3 daysEvent list; unread badges; preferences
2.11KCMHomePageMEDIUM3–4 daysHub page; PDF file access
2.12JumpseatAirlinePolicyPageMEDIUM2–3 daysPolicy HTML display
2.13JumpseatFlightFinderSavedFlightsPageMEDIUM3–4 daysSaved list; connectivity; notification opt-in
2.14DocumentsListPageMEDIUM3–4 daysScoped doc list; unread; offline
2.15InCaseOfAccidentPage (dynamic)MEDIUM2–3 daysDynamic content variant
2.16CheckboxControlMEDIUM1–2 daysCustom Razor checkbox with @bind and EventCallback

Tier 3 — Defer 17 pages/controls

High complexity. Significant refactoring required before migration. Total estimated effort: 8–10 weeks.

3.1 SettingsPage — HARD

320+ lines XAML, 5+ commands, geolocation, permissions, messenger. Extract geolocation into IGeolocationService; replace messenger with state container; replace Expander with HTML accordion. Effort: 4–5 days

3.2 HomePage — HARD

Server-driven layout with drag-and-drop tile reordering, 7+ messenger registrations. Extract home layout into service; replace drag-and-drop with CSS drag API; replace messenger with state container. Effort: 5–7 days

3.3 BannerPage — MEDIUM-HARD

Web content loading with 4-step single-threaded render pipeline. Extract loading logic; replace CollectionView with Razor list; replace unread badge with Razor component. Effort: 4–5 days

3.4 DocumentCenterPage — HARD

Grouped CollectionView with 5-scope document hierarchy. Grouped list → nested Razor components; auth guards → Blazor AuthorizeView; extract ShowContent(). Effort: 4–5 days

3.5 PushNotificationsListPage — HARD

330+ line ViewModel, 5-way content switch (Document / WebLink / DeepLink / FlightAlert / Unknown). Extract into IContentRouterService; content routing from Razor to native page. Effort: 4–5 days

3.6 AdvocacyPage — HARD

250+ lines XAML, PAC membership tiers, 4 WebView targets. PAC tier display → @switch; WebView targets → inline HTML or <iframe>; parallel data loading via Task.WhenAll. Effort: 5–7 days

3.7 InternationalDirectoryPage — HARD

Grouped CollectionView, background filtering, country detail HTML, platform SearchBar. Grouped list → continent/country nesting; background filtering → debounced component-level search; country detail → @((MarkupString)html). Effort: 3–4 days

3.8 MyPilotGroupPage — HARD

Heavy messenger usage (DocumentsReady, EventsReady), Launcher. Replace all messenger registrations with state container subscriptions; grouped menu → nested Razor; unread badges → reusable Razor badge component. Effort: 4–5 days

3.9 FTDTFlightDutyPeriodListPage — HARD

445 lines XAML, Expander + SwipeView + grouped CollectionView, Thread.Sleep in background tasks. Replace Expander with <details>/<summary>; replace SwipeView with action buttons; remove Thread.SleepTask.Delay. Effort: 5–7 days

3.10 FTDTDutyDetailsPage — HARD

400 lines XAML, SegmentedControl + DatePicker, 25+ bindings, timezone matching, scroll handlers. Segment state machine → @switch; replace scroll commands with JS scrollIntoView; timezone → ITimeZoneService. Effort: 5–7 days

3.11 JumpseatFlightFinderDetailsPage — HARD

Reflection-based property updates (GetProperties().Where(...)). Replace reflection with explicit property setters; policy WebView → inline HTML. Effort: 3–4 days

3.12 JumpseatFlightFinderResultsFiltersPage — HARD

Toolkit Expander, Messenger pattern. State container replacing FilterSettingsHaveChangedMessage; Expander → HTML accordion; modal → Blazor overlay. Effort: 3–4 days

3.13 JumpseatFlightFinderResultsPage — HARD

800+ line ViewModel, pagination, sorting, SwipeView, reflection-based property sync. Replace SwipeView with action buttons; pagination → load-more pattern; replace reflection with explicit mapping. Effort: 5–7 days

3.14 PilotGroupHotelsPage — HARD

Platform-specific map URLs (geo: vs maps.apple.com), grouped collection. IMapLauncherService abstraction; grouped → nested Razor; complex filtering → component state. Effort: 3–4 days

3.15 JumpseatBottomMenu — HARD (custom control)

Code-behind-driven state, icon manipulation, navigation logic. Replace with Razor tab/navigation component; state → component parameters + EventCallbacks; icons → CSS classes. Effort: 2–3 days

3.16 FTDTStartInfoView — HARD (custom control)

Complex form with timezone selection, nested conditional sections. Timezone → <select> from ITimeZoneService; conditional sections → @if; date/time → HTML5 inputs. Effort: 3–4 days

3.17 TimePickerControl — MEDIUM (custom control)

Required by FTDT pages — migrate early. Replace with HTML5 <input type="time"> + Blazor @bind; expose ValueChanged EventCallback. Effort: 1 day

Tier 4 — Defer Late 5 pages

Very high complexity. Require architectural decisions, significant refactoring, or deep inheritance hierarchies. Total estimated effort: 4–6 weeks.

4.1 FTDTDutyPeriodBasePage — VERY HARD

500+ line XAML ControlTemplate, reflection-based view discovery (GetTemplateChild()), base class for 3 duty period variants. Decompose into 5+ child Razor components; replace inheritance with composition (RenderFragment); replace reflection with explicit property updates; replace scroll commands with JS scrollIntoView. Effort: 8–10 days

4.2 FTDTCADutyPeriodDetailsPage — VERY HARD

650+ lines XAML, most complex FTDT page, event subscription chains (DutyPeriod → DutyCollection → Child), reserve operation branching. Break into 6+ child Razor components; replace event chains with cascading parameters; replace SwipeView with action buttons. Effort: 8–10 days

4.3 JumpseatFlightFinderSearchPage — VERY HARD

1100+ line ViewModel (largest in the app), geolocation, debounced airport search, date range restrictions, CancellationTokenSource pattern (Feature #1839). Extract into IAirportSearchService, IFlightSearchService, IGeolocationService; search form → Blazor EditForm; airport picker → autocomplete with debounce. Effort: 8–12 days

4.4 JumpseatToolPage — VERY HARD

Entire UI built programmatically (no XAML); 16 specialized card ViewModels. Convert to declarative Razor component tree; each card ViewModel → individual Razor component. Effort: 4–5 days

4.5 PDFJSPage — HARD

Platform-specific WebView configuration for local PDF files. Decision required: (A) Keep as native page — 0 days; (B) pdf.js via JS interop in Blazor component — 3–5 days; (C) Server-side PDF rendering. Recommendation: Option A during straggler period; revisit after Tier 3.

Tier 5 — Keep Native 5 pages

These remain as native MAUI pages permanently. Blazor components navigate to them via INavigationService.

PageReasonStrategy
LoginPageMSAL + JWT dual-mode, 10+ error scenarios, SecureStorage, MSAL PCA, platform browser. Auth failure = total app failure.Keep native. Blazor components consume auth state via injected IAuthenticationService.
AutoLoginWebViewContentView11-stage state machine with platform JS bridges ([JavascriptInterface] on Android, WKScriptMessageHandler on iOS). These break inside BlazorWebView.Keep as native control. Only used by LoginPage and WebViewPage.
WebViewPageGeneral-purpose web content viewer used by 10+ pages; deep AutoLogin integration; FAB UI; conditional visibility.Keep native initially. Blazor navigates to it via INavigationService. Revisit after Tier 3 determines how much WebView content can be inlined into Razor.
AppShellApp-level navigation container; Shell flyout; 47 registered routes; dynamic menu.MAUI Shell remains the navigation host. Blazor components live inside BlazorWebView within Shell pages.
UnreadContentView / UnreadCountContentViewReusable badge indicators used by both native and Blazor pages during transition.Create Razor equivalent for Blazor pages; keep native version for unmigrated pages. Deprecate native after all consumers migrate.

Service Extraction Roadmap

Before pages in Tiers 2–4 can migrate, these platform-abstraction services must be created:

Priority 1 — Required for Tier 1

ServiceWrapsUsed by
INavigationServiceShell.GoToAsync, NavigationManagerAll pages
IDialogServiceDisplayAlert, DisplayActionSheetMost pages
ILauncherServiceLauncher.OpenAsync8+ pages

Priority 2 — Required for Tier 2

ServiceWrapsUsed by
IClipboardServiceClipboard.SetTextAsyncPushNotificationsSettings
IConnectivityServiceConnectivity.CurrentKCMAirportDetails, SavedFlights
IPreferencesServiceAppProperties, SettingsPilotGroupEvents, HomePageSettings
IDeviceInfoServiceDeviceInfo.Idiom, DeviceDisplayMembershipCard, OrangeCard
IFileAccessServicePlatform PDF pathsKCMHomePage

Priority 3 — Required for Tier 3

ServiceWrapsUsed by
IGeolocationServiceGeolocation, PermissionsSettingsPage, FlightFinderSearch
IPermissionsServicePermissions.RequestAsyncSettingsPage, FlightFinderSearch
IMapLauncherServicePlatform map URLsPilotGroupHotels
IContentRouterServiceShowContent 5-way switchPushNotificationsList
ITimeZoneServiceTimeZoneHelpersFTDT pages
IFTDTCalculatorServiceFTDTCalculator_USA/CANFTDT pages
AppStateContainerWeakReferenceMessenger10+ pages

Priority 4 — Required for Tier 4

ServiceWrapsUsed by
IAirportSearchServiceAirport location searchFlightFinderSearch
IFlightSearchServiceSearch execution, cancellationFlightFinderSearch
IDutyPeriodFactoryDutyPeriod type creationFTDTOperationTypeSelection

Known Traps

Cross-References

DocumentLocation
LOE estimate and straggler-migration patternblazor-hybrid-migration-estimate.html
N-Tier Architecture Refactor (prerequisite)n-tier-orchestration-handoff.html
Component specifications (AB#1821 baseline)component-specifications/index.html
UI Refresh epic roadmapdetail/roadmap.html