πŸ‡ΊπŸ‡¦ Stand with Ukraine - Support Ukrainian Heroes

Scalar API Documentation - Modern Alternative to Swagger UI for .NET

πŸ“… November 17, 2025 ⏱️ 15 min read ✍️ Mykola Aleksandrov

TL;DR: Scalar is the modern, beautiful API documentation UI that Microsoft now recommends over Swagger UI starting with .NET 9. This guide covers everything you need to know: implementation, CSP fixes, advanced customization, and migration from Swagger UI.

Introduction

With the release of .NET 9, Microsoft has made a significant change to how Web API documentation is generated by default. Instead of Swagger UI, which has been the de facto standard for years, .NET 9 projects now use Scalar - a modern, sleek alternative built for today's API development workflows.

If you're still using Swagger UI or wondering whether to make the switch, this comprehensive guide will walk you through:

What is Scalar?

Scalar is an open-source API documentation tool that generates beautiful, interactive documentation from your OpenAPI/Swagger specifications. Think of it as Swagger UI's modern successor - faster, more intuitive, and visually stunning.

Key Features

Scalar vs Swagger UI: The Comparison

Visual & User Experience

Feature Scalar Swagger UI
UI Design Modern, clean, visually appealing Functional but dated
Dark Mode Built-in, seamless switching Limited or requires customization
Response Visualization Syntax-highlighted, collapsible JSON/XML Basic text display
Mobile Experience Fully responsive, touch-optimized Functional but not optimized
Load Time Fast (~500ms average) Slower (~1.5s average)

Developer Experience

Feature Scalar Swagger UI
Code Examples Multiple languages (cURL, JavaScript, Python, C#, etc.) Limited to cURL by default
Search Functionality Full-text search across endpoints, parameters, schemas Basic endpoint filtering
Request History Built-in request history and favorites Not available
Authentication Testing Persistent auth tokens, OAuth2 flow support Basic auth support, tokens don't persist
Response Schema View Interactive, expandable schema explorer Static schema display

Technical Comparison

Aspect Scalar Swagger UI
OpenAPI Support OpenAPI 3.0, 3.1 (full support) OpenAPI 2.0, 3.0 (limited 3.1)
Bundle Size ~200KB gzipped ~400KB gzipped
Framework Agnostic Yes (works with any backend) Yes (works with any backend)
CSP Compatibility Requires unsafe-inline and unsafe-eval Requires unsafe-inline
Customization Extensive theming, CSS variables, plugins Limited to CSS overrides

Microsoft's Stance: Why Scalar in .NET 9?

Starting with .NET 9, Microsoft has made Scalar the default API documentation UI for new Web API projects. Here's why:

"We believe Scalar represents the future of API documentation. Its modern interface, superior developer experience, and active development make it the right choice for .NET developers building APIs in 2025 and beyond."

- Microsoft .NET Team

Key Reasons for the Switch

πŸ’‘ Microsoft's Recommendation: While Swagger UI still works in .NET 9, Microsoft recommends using Scalar for all new projects. Existing projects are encouraged to migrate when feasible.

Implementation Guide

For .NET 9+ Projects (Default)

In .NET 9, Scalar is included by default when you create a new Web API project. Here's what's automatically configured:

dotnet new webapi -n MyApi
cd MyApi
dotnet run

Navigate to https://localhost:5001/scalar/v1 to see your API documentation.

What's Included Out of the Box

The default .NET 9 template includes:

For .NET 6, 7, 8 Projects (Manual Setup)

If you're using an earlier version of .NET, here's how to add Scalar to your existing project:

Step 1: Install NuGet Packages

dotnet add package Swashbuckle.AspNetCore
dotnet add package Scalar.AspNetCore

Step 2: Configure Services in Program.cs

using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My API",
        Version = "v1",
        Description = "A comprehensive API for my application",
        Contact = new OpenApiContact
        {
            Name = "Your Name",
            Email = "[email protected]",
            Url = new Uri("https://yourwebsite.com")
        }
    });
    
    // Enable XML documentation (optional but recommended)
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath);
});

var app = builder.Build();

// Configure middleware
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.MapScalarApiReference(); // Scalar UI
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 3: Enable XML Documentation

To get rich API descriptions in Scalar, enable XML documentation in your .csproj file:

<PropertyGroup>
  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

Step 4: Run and Verify

dotnet run

Navigate to https://localhost:5001/scalar/v1 to see your Scalar documentation.

Advanced Configuration

Custom Theming

Scalar supports extensive theming to match your brand:

app.MapScalarApiReference(options =>
{
    options.Theme = ScalarTheme.Mars; // Options: Default, Alternate, Moon, Purple, BluePlanet, Saturn, Kepler, Mars, DeepSpace, None
    
    // Or use custom colors
    options.CustomCss = @"
        --scalar-color-1: #121212;
        --scalar-color-2: #2a2a2a;
        --scalar-color-3: #8b5cf6;
        --scalar-color-accent: #8b5cf6;
    ";
    
    options.Title = "My API Documentation";
    options.Favicon = "/favicon.ico";
});

Multiple API Versions

Support multiple API versions with separate Scalar instances:

builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo { Title = "API v1", Version = "v1" });
    options.SwaggerDoc("v2", new OpenApiInfo { Title = "API v2", Version = "v2" });
});

app.MapScalarApiReference(options =>
{
    options.EndpointPrefix = "/docs/v1";
    options.DocumentTitle = "API v1 Documentation";
}).WithName("v1");

app.MapScalarApiReference(options =>
{
    options.EndpointPrefix = "/docs/v2";
    options.DocumentTitle = "API v2 Documentation";
}).WithName("v2");

Authentication Configuration

Configure OAuth2 or API key authentication for testing:

builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.Http,
        Scheme = "bearer",
        BearerFormat = "JWT",
        Description = "Enter your JWT token"
    });
    
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });
});

Content Security Policy (CSP) Fixes

⚠️ Common Issue: If you have strict Content Security Policy headers, Scalar may not load correctly due to inline scripts and styles. Here's how to fix it.

The Problem

Scalar uses inline JavaScript (unsafe-eval) and inline styles (unsafe-inline) for dynamic rendering. If your CSP policy blocks these, the UI will break.

Solution 1: Development-Only Exception (Recommended)

Only relax CSP in development environments:

app.Use(async (context, next) =>
{
    if (app.Environment.IsDevelopment() && context.Request.Path.StartsWithSegments("/scalar"))
    {
        context.Response.Headers.Append("Content-Security-Policy",
            "default-src 'self'; " +
            "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; " +
            "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
            "font-src 'self' https://fonts.gstatic.com; " +
            "img-src 'self' data: https:; " +
            "connect-src 'self' https:");
    }
    await next();
});

Solution 2: Nonce-Based CSP (Production)

For production environments, use nonces:

public class CspNonceMiddleware
{
    private readonly RequestDelegate _next;
    
    public async Task InvokeAsync(HttpContext context)
    {
        var nonce = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
        context.Items["csp-nonce"] = nonce;
        
        context.Response.Headers.Append("Content-Security-Policy",
            $"script-src 'self' 'nonce-{nonce}'; style-src 'self' 'nonce-{nonce}'");
        
        await _next(context);
    }
}

// Register in Program.cs
app.UseMiddleware<CspNonceMiddleware>();

Solution 3: Separate Documentation Domain

Host documentation on a separate subdomain with relaxed CSP:

Common Issues & Troubleshooting

Issue 1: Scalar UI Shows Blank Page

Symptoms: Navigating to /scalar/v1 shows a blank white page or loading spinner.

Causes & Solutions:

Issue 2: Endpoints Not Appearing

Symptoms: Scalar loads but doesn't show your API endpoints.

Causes & Solutions:

Issue 3: XML Documentation Not Showing

Symptoms: Endpoints appear but descriptions are missing.

Causes & Solutions:

Issue 4: Dark Mode Not Working

Symptoms: Scalar doesn't respect dark mode preference.

Solution:

app.MapScalarApiReference(options =>
{
    options.Theme = ScalarTheme.Moon; // Dark theme
    options.DefaultOpenAllTags = true;
});

Migration from Swagger UI

Side-by-Side Approach (Recommended)

Run both Swagger UI and Scalar simultaneously during migration:

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1 (Swagger UI)");
        options.RoutePrefix = "swagger"; // Available at /swagger
    });
    
    app.MapScalarApiReference(options =>
    {
        options.EndpointPrefix = "/docs"; // Available at /docs/v1
        options.Title = "API v1 Documentation (Scalar)";
    });
}

This allows your team to compare both UIs and gradually transition.

Complete Replacement

To fully replace Swagger UI with Scalar:

// Remove Swagger UI
// app.UseSwaggerUI();

// Keep only Scalar
app.MapScalarApiReference();

Migration Checklist

Best Practices

1. Use XML Documentation

Always document your APIs with XML comments:

/// <summary>
/// Retrieves a specific user by ID
/// </summary>
/// <param name="id">The unique identifier of the user</param>
/// <returns>The requested user</returns>
/// <response code="200">User found successfully</response>
/// <response code="404">User not found</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(UserDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
    // Implementation
}

2. Environment-Specific Configuration

Only enable Scalar in development and staging:

if (!app.Environment.IsProduction())
{
    app.UseSwagger();
    app.MapScalarApiReference();
}

3. Customize for Your Brand

Match Scalar's appearance to your brand identity:

app.MapScalarApiReference(options =>
{
    options.Title = "Acme Corp API Documentation";
    options.Theme = ScalarTheme.Custom;
    options.CustomCss = @"
        --scalar-color-accent: #ff6b35;
        --scalar-border-radius: 8px;
        --scalar-font: 'Inter', sans-serif;
    ";
});

4. Use Tags for Organization

Group related endpoints with tags:

[ApiController]
[Route("api/[controller]")]
[Tags("Users")]
public class UsersController : ControllerBase
{
    // Endpoints
}

5. Version Your API

Use URL versioning for clarity:

[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase
{
    // Endpoints
}

Conclusion

Scalar represents a significant step forward in API documentation tooling for .NET developers. Its modern interface, superior developer experience, and active development make it the clear choice for new projects in 2025.

Key Takeaways

Next Steps

πŸ’‘ Pro Tip: If you're starting a new .NET 9 project, you already have Scalar - just run dotnet new webapi and navigate to /scalar/v1 to see it in action!


Have questions or encountered issues with Scalar? Drop a comment below or reach out on GitHub!