Note: ShellUI is in alpha with 100 components. Test and provide feedback!
ShellUI is a CLI-first Blazor component library inspired by shadcn/ui. Instead of installing a NuGet package, you use a CLI tool to copy components directly into your project, giving you full control and customization capabilities.
- .NET 8.0 or 9.0 SDK or higher
- No Node.js required (Tailwind standalone CLI)
- A Blazor project (Server, WASM, or SSR)
dotnet tool install -g ShellUI.CLINavigate to your Blazor project directory:
cd YourBlazorProject
dotnet shellui initThis will:
- Detect your project type (Server/WASM/SSR)
- Create
Components/UI/folder structure - Set up Tailwind CSS v4
- Create
shellui.jsonconfiguration - Update necessary files
You'll see output like:
✓ Detected project type: Blazor Server
✓ Created Components/UI/ directory
✓ Initialized Tailwind CSS v4
✓ Created shellui.json configuration
✓ Updated _Imports.razor
ShellUI is ready! Add your first component:
dotnet shellui add button
dotnet shellui add buttonThis copies the Button component to Components/UI/Button.razor in your project.
# Space-separated
dotnet shellui add button card alert dialog
# Comma-separated
dotnet shellui add button,card,alert,dialog
# Mix both (why not!)
dotnet shellui add button,card alert dialog
# Add many at once
dotnet shellui add button,input,label,card,alert,badge,skeleton,separatorShellUI automatically resolves and installs dependencies:
dotnet shellui add dialog
# Also installs: button (dependency)After adding components, use them in your Razor pages:
@page "/example"
<div class="container mx-auto p-4">
<Card>
<CardHeader>
<CardTitle>Welcome to ShellUI</CardTitle>
<CardDescription>
Build beautiful Blazor apps with ease
</CardDescription>
</CardHeader>
<CardContent>
<p class="mb-4">ShellUI provides production-ready components that you own.</p>
<Input Placeholder="Enter your email" Type="email" />
</CardContent>
<CardFooter>
<Button OnClick="HandleSubscribe">Subscribe</Button>
</CardFooter>
</Card>
</div>
@code {
private void HandleSubscribe()
{
// Your logic here
}
}Since components are copied to your project, you can customize them freely:
- Open
Components/UI/Button.razor - Modify the Tailwind classes
- Add new variants
- Change behavior
- It's YOUR code!
Example customization:
@* Components/UI/Button.razor *@
@code {
// Add a new "neon" variant
private string BuildCssClass()
{
var classes = new List<string> { /* ... base classes ... */ };
classes.Add(Variant switch
{
"default" => "bg-primary text-primary-foreground",
"neon" => "bg-gradient-to-r from-pink-500 to-purple-500 text-white shadow-neon",
// ... other variants
_ => "bg-primary text-primary-foreground"
});
return string.Join(" ", classes);
}
}Then use it:
<Button Variant="neon">Neon Button</Button>dotnet shellui listOutput: A table showing component name, status (installed/available), version, category, and description. Run dotnet shellui list to see all ~100 components.
dotnet shellui list --installed# Update a specific component
dotnet shellui update button
# Update all components
dotnet shellui update --allNote: If you've customized a component, you'll be warned before updating.
dotnet shellui diff buttonShows what's different between your version and the latest version.
dotnet shellui remove buttonRemoves the component file. You'll be warned if other components depend on it.
ShellUI uses Tailwind CSS v4 with CSS variables for theming.
Edit wwwroot/styles/input.css:
@layer base {
:root {
--primary: 262 83% 58%; /* Change primary color */
--primary-foreground: 0 0% 100%;
/* ... other colors ... */
}
.dark {
--primary: 263 70% 50%; /* Dark mode primary */
--primary-foreground: 0 0% 100%;
/* ... other colors ... */
}
}ShellUI includes automatic dark mode support. Toggle via:
@inject IThemeService Theme
<Button OnClick="() => Theme.ToggleThemeAsync()">
Toggle Theme
</Button>Or manually set:
await Theme.SetThemeAsync("dark");
await Theme.SetThemeAsync("light");
await Theme.SetThemeAsync("system"); // Follow system preferencedotnet new blazor -o MyApp
cd MyApp
dotnet shellui init
dotnet shellui add button card
dotnet rundotnet new blazorwasm -o MyApp
cd MyApp
dotnet shellui init
dotnet shellui add button card
dotnet rundotnet new blazor -o MyApp --interactivity None
cd MyApp
dotnet shellui init
dotnet shellui add button card
dotnet run@page "/register"
<Card class="max-w-md mx-auto">
<CardHeader>
<CardTitle>Create Account</CardTitle>
</CardHeader>
<CardContent>
<EditForm Model="@model" OnValidSubmit="HandleRegister">
<DataAnnotationsValidator />
<div class="space-y-4">
<div>
<Label For="email">Email</Label>
<Input @bind-Value="model.Email" Type="email" Id="email" />
<ValidationMessage For="() => model.Email" />
</div>
<div>
<Label For="password">Password</Label>
<Input @bind-Value="model.Password" Type="password" Id="password" />
<ValidationMessage For="() => model.Password" />
</div>
<Button Type="submit" IsLoading="@isSubmitting">
Register
</Button>
</div>
</EditForm>
</CardContent>
</Card>
@code {
private RegisterModel model = new();
private bool isSubmitting;
private async Task HandleRegister()
{
isSubmitting = true;
try
{
// Your registration logic
await Task.Delay(1000); // Simulate API call
}
finally
{
isSubmitting = false;
}
}
public class RegisterModel
{
[Required, EmailAddress]
public string Email { get; set; } = "";
[Required, MinLength(8)]
public string Password { get; set; } = "";
}
}<Button OnClick="() => showDialog = true">Open Dialog</Button>
<Dialog Open="@showDialog" OnOpenChange="(open) => showDialog = open">
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button Variant="outline" OnClick="() => showDialog = false">
Cancel
</Button>
<Button Variant="destructive" OnClick="HandleConfirm">
Confirm
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
@code {
private bool showDialog;
private void HandleConfirm()
{
// Your logic
showDialog = false;
}
}<Card>
<CardHeader>
<CardTitle>Users</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
<TableHead>Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@foreach (var user in users)
{
<TableRow>
<TableCell>@user.Name</TableCell>
<TableCell>@user.Email</TableCell>
<TableCell>
<Badge>@user.Role</Badge>
</TableCell>
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger>
<Button Variant="ghost" Size="sm">•••</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem OnClick="() => EditUser(user)">
Edit
</DropdownMenuItem>
<DropdownMenuItem OnClick="() => DeleteUser(user)">
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</CardContent>
</Card>dotnet shellui add button input label card alertBuild complex UIs by composing simple components together.
Don't be afraid to modify components - they're yours!
During development:
npm run css:watchStick to the default variants (default, outline, ghost, etc.) for consistency.
Components are accessible by default - keep it that way when customizing.
Especially if you've customized components significantly.
dotnet shellui update --all# Rebuild Tailwind CSS
npm run css:build
# Or in watch mode
npm run css:watch# Make sure you're in the project directory
cd YourBlazorProject
# List available components
dotnet shellui listMake sure _Imports.razor includes:
@using YourProject.Components.UI- Check
wwwroot/styles/input.csshas dark mode variables - Ensure theme toggle component is implemented
- Verify
<html>or<body>tag getsdarkclass applied
- Documentation: https://shellui.dev/docs (Coming soon)
- GitHub Issues: https://github.com/shellui-dev/shellui/issues
- Discussions: https://github.com/shellui-dev/shellui/discussions
- Explore all available components:
dotnet shellui list - Check out examples: https://shellui.dev/examples (Coming soon)
- Join the community: Discord link (Coming soon)
- Star the repo: https://github.com/shellui-dev/shellui
Remember: ShellUI is currently in development. This guide represents the planned developer experience for v1.0. Star the repo to follow progress!