Typed IDs for UI Elements
Neos provides strongly-typed identifiers for UI elements to improve code safety, maintainability, and developer experience. These typed IDs replace hard-coded strings and nameof() expressions throughout the codebase.
Overview
The framework automatically generates several static classes containing typed identifiers during the build process:
UIViews- Contains identifiers for all UI views in your applicationImages- Contains identifiers for all images and iconsReports- Contains identifiers for all reportsThemes- Contains identifiers for all themes
Benefits
Type Safety
Compile-time validation prevents typos and ensures referenced elements actually exist in your project.
// Compile error if CustomerListUI doesn't exist
new NavigationOptions(UIViews.CustomerListUI)
// Runtime error possible with strings
new NavigationOptions("CustomerListUI") // Typo won't be caught at compile time
Dependency Detection
Typed IDs enable the Dependency Viewer in Neos Studio to more reliably detect and analyze dependencies between elements. While simple string literals can be detected, typed IDs provide stronger guarantees and make complex scenarios easier to track.
// ✅ Always reliably detected by dependency analysis
new NavigationOptions(UIViews.CustomerListUI)
// ✅ Can be detected when using direct string literals
new NavigationOptions("CustomerListUI")
// ❌ Harder to track when using dynamic strings
string viewName = condition ? "CustomerListUI" : "CustomerEditUI";
new NavigationOptions(viewName)
// ✅ Better approach for conditional scenarios
NavigationOptions options = new(condition ? UIViews.CustomerListUI : UIViews.CustomerEditUI);
IntelliSense Support
Auto-completion helps discover available options and reduces the need to remember exact names.
Clear Documentation
The relationship between code and UI elements becomes explicit and self-documenting.
Available Typed ID Classes
UI views
Contains static readonly properties for all UI views in your application:
public static class UIViews
{
/// <summary>
/// CustomerListUI.
/// </summary>
public static readonly UIViewId CustomerListUI = null!;
/// <summary>
/// ProductEditUI.
/// </summary>
public static readonly UIViewId ProductEditUI = null!;
// ... other UI views
}
Usage examples:
// Navigation
new NavigationOptions(UIViews.CustomerListUI)
Images
Contains static readonly properties for all images and icons:
public static class Images
{
/// <summary>
/// Settings.
/// </summary>
public static readonly ImageId Settings = null!;
/// <summary>
/// UserProfile.
/// </summary>
public static readonly ImageId UserProfile = null!;
// ... other images
}
Usage examples:
// Icon assignment
.IconId = Images.Settings
// Method calls
.WithIconId(Images.UserProfile)
// Action icons
Actions.SaveData.IconId = Images.Save;
Reports
Contains static readonly properties for all reports:
public static class Reports
{
/// <summary>
/// InvoiceReport.
/// </summary>
public static readonly ReportId InvoiceReport = null!;
/// <summary>
/// CustomerStatement.
/// </summary>
public static readonly ReportId CustomerStatement = null!;
// ... other reports
}
Usage examples:
// Report execution
await ExecuteReportOptions(Reports.InvoiceReport);
Themes
Contains static readonly properties for all themes:
public static class Themes
{
/// <summary>
/// NeosStudio.
/// </summary>
public static readonly ThemeId NeosStudio = null!;
/// <summary>
/// NeosStudioDark.
/// </summary>
public static readonly ThemeId NeosStudioDark = null!;
// ... other themes
}
Usage examples:
// Theme switching
await ThemeManager.SetThemeAsync(Themes.NeosStudioDark);
// Theme comparison
if (ThemeManager.GetCurrentTheme().Id == Themes.NeosStudio.Name)
{
// Light theme logic
}
Common Usage Patterns
Navigation
Before (hard-coded strings):
new NavigationOptions("CustomerListUI")
new NavigationOptions(nameof(CustomerEditUI))
After (typed IDs):
new NavigationOptions(UIViews.CustomerListUI)
new NavigationOptions(UIViews.CustomerEditUI)
Icon Assignment
Before (string literals):
.IconName = "Settings"
.WithIcon("UserProfile")
Actions.Save.IconName = "save-icon";
After (typed IDs):
.IconId = Images.Settings
.WithIconId(Images.UserProfile)
Actions.Save.IconId = Images.SaveIcon;
Conditional Logic
Before:
if (currentView == "CustomerListUI")
{
// Logic specific to customer list
}
After:
if (currentView == UIViews.CustomerListUI.Name)
{
// Logic specific to customer list
}
Migration from Legacy Code
Identifying Legacy Patterns
Look for these patterns in your codebase:
new NavigationOptions("ViewName")→ UseUIViews.ViewName.IconName = "iconName"or.WithIcon("iconName")→ UseImages.IconNameand.WithIconId()new NavigationOptions(nameof(ViewName))→ UseUIViews.ViewName
Automated Migration
For bulk conversion, use the PowerShell migration script available in the Neos repository:
Script Location: convert-ui-string-literals.ps1
This script serves as an example that you can adapt for your own cluster's migration needs.
Example usage:
# Navigate to your cluster directory
cd C:\YourCluster
# Download and adapt the script from the Neos repository
# Then run with preview mode first to see what changes would be made
.\convert-ui-string-literals.ps1 -Directories @("modules\YourModule1", "modules\YourModule2") -WhatIf
# Apply changes
.\convert-ui-string-literals.ps1 -Directories @("modules\YourModule1", "modules\YourModule2")
# Apply changes with backup and verbose output
.\convert-ui-string-literals.ps1 -Directories @("modules\YourModule1", "modules\YourModule2") -CreateBackup -VerboseOutput
Best Practices
Use Typed IDs Consistently
Always prefer typed IDs over string literals for better type safety, dependency tracking, and maintainability:
// ✅ Good - Type-safe and always trackable by dependency analysis
new NavigationOptions(UIViews.CustomerListUI)
.WithIconId(Images.Settings)
// ⚠️ Less reliable - Direct strings can be detected but dynamic scenarios are problematic
new NavigationOptions("CustomerListUI")
.WithIcon("settings")
// ❌ Avoid - Very difficult to track in dependency analysis
string viewName = condition ? "CustomerListUI" : "CustomerEditUI";
new NavigationOptions(viewName)
// ✅ Better approach for conditional scenarios
NavigationOptions options = new(condition ? UIViews.CustomerListUI : UIViews.CustomerEditUI);
Leverage IntelliSense
Take advantage of auto-completion to discover available options:
// Type "UIViews." and use IntelliSense to see all available views
var navigation = new NavigationOptions(UIViews.
Handle Dynamic Scenarios
For cases where UI element names are determined at runtime, use the FromName() method instead of string literals:
// ✅ Correct approach for dynamic names
string dynamicViewName = $"Customer{type}UI";
NavigationOptions options = new(UIViewId.FromName(dynamicViewName));
// Same pattern for other ID types
ImageId dynamicIconId = ImageId.FromName($"Status{status}Icon");
ReportId dynamicReportId = ReportId.FromName($"{category}Report");
Warning
Methods accepting string parameters directly (e.g., new NavigationOptions(string)) are obsolete and should be avoided. Always use the typed ID classes and their FromName() method for dynamic scenarios.