Umbraco comes with a default Welcome dashboard in the backoffice. It displays news from Umbraco HQ along with handy links to documentation, training, and other community resources.
That’s great for developers, but when you’re building Umbraco sites for clients, those links are often not the most relevant. In fact, the traditional recommendation has often been to simply remove the Welcome dashboard entirely.
But with the current backoffice (which I should probably stop calling new by now), there are plenty of extension points - and the Welcome dashboard is one of them.
So instead of removing it, why not use it for your own content?
Extending the Welcome dashboard
The Welcome dashboard is powered by a service in the backend, and you can replace or extend its behaviour by implementing the INewsDashboardService interface in your own C# code.
Once you do that, Umbraco will use your implementation to populate the dashboard.
All you need to return is a NewsDashboardResponseModel, which contains a collection of items to display. Each item supports:
- A header
- Body text
- An image (URL + alt text)
- A link URL
- Button text
- A priority (High, Medium, or Low)
The dashboard will automatically sort the items by priority before rendering them.
A (slightly useless) example implementation
As a simple demonstration, here’s an intentionally useless implementation that displays the weather from 12 random locations on Earth every time the dashboard loads.
using System.Text.Json;
using Umbraco.Cms.Api.Management.Services.NewsDashboard;
using Umbraco.Cms.Api.Management.ViewModels.NewsDashboard;
public class UselessNewsDashboardService : INewsDashboardService
{
private readonly HttpClient _httpClient;
private readonly AppCaches _appCaches;
public GiNewsDashboardService(HttpClient httpClient, AppCaches appCaches)
{
_httpClient = httpClient;
_appCaches = appCaches;
}
public async Task<NewsDashboardResponseModel> GetItemsAsync()
{
// fetch some data - this depends on your usecase!
var locations =
await _appCaches.RuntimeCache.GetCacheItemAsync(
"randomWeather",
async () => await GetWeatherFromRandomLocationsAsync(),
TimeSpan.FromHours(1)
) ?? [];
// build the response model
var response = new NewsDashboardResponseModel()
{
Items = locations.Select(x => new NewsDashboardItemResponseModel()
{
Header = x.Title,
Priority = "High", // or Medium or Low
Url = x.Url,
ImageUrl = x.ImageUrl,
Body = x.ShortDescription,
ButtonText = "Go to website",
}),
};
return response;
}
// below are methods for communicating with the weather API
// - you don't need these
public async Task<WeatherResult> GetWeatherAsync(double lat, double lon)
{
// this is just to prevent 529 Too many requests
await Task.Delay(Random.Shared.Next(100, 1000));
var url =
$"https://api.open-meteo.com/v1/forecast"
+ $"?latitude={lat}&longitude={lon}"
+ $"¤t_weather=true";
var json = await _httpClient.GetStringAsync(url);
using var doc = JsonDocument.Parse(json);
var current = doc.RootElement.GetProperty("current_weather");
var temperature = current.GetProperty("temperature").GetDouble();
var weatherCode = current.GetProperty("weathercode").GetInt32();
var description = GetDescription(weatherCode);
var imageUrl = GetImageUrl(weatherCode);
return new WeatherResult(
Title: $"{description}, {temperature}°C",
ShortDescription: $"Weather at {lat}/{lon}",
ImageUrl: imageUrl,
Url: $"https://www.open-meteo.com/en/forecast?latitude={lat}&longitude={lon}"
);
}
public record WeatherResult(string Title, string ShortDescription, string ImageUrl, string Url);
public static string GetDescription(int code) =>
code switch
{
0 => "Clear sky",
1 or 2 or 3 => "Partly cloudy",
45 or 48 => "Fog",
51 or 53 or 55 => "Drizzle",
61 or 63 or 65 => "Rain",
71 or 73 or 75 => "Snow",
80 or 81 or 82 => "Rain showers",
95 => "Thunderstorm",
_ => "Unknown weather",
};
public static string GetImageUrl(int code) =>
code switch
{
0 => "https://cdn-icons-png.flaticon.com/512/869/869869.png",
1 or 2 or 3 => "https://cdn-icons-png.flaticon.com/512/1163/1163661.png",
61 or 63 or 65 => "https://cdn-icons-png.flaticon.com/512/3351/3351979.png",
71 or 73 or 75 => "https://cdn-icons-png.flaticon.com/512/642/642102.png",
95 => "https://cdn-icons-png.flaticon.com/512/1146/1146869.png",
_ => "https://cdn-icons-png.flaticon.com/512/1779/1779940.png",
};
}
You also have to register the service with a Composer, like so:
using Umbraco.Cms.Core.Composing;
public class UselessNewsDashboardComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.Services.AddSingleton<INewsDashboardService, UselessNewsDashboardComposer >();
}
}
And there you have it:
This example obviously isn’t very useful - but it shows how little code is required to take control of the dashboard content.
A practical use case
Where this does become useful is for agencies and teams building Umbraco sites for clients.
Instead of showing Umbraco HQ news, you can use the Welcome dashboard to:
- Share important project updates
- Announce new features or releases
- Provide onboarding instructions
- Link to internal documentation or support channels
- Communicate maintenance windows or known issues
In other words, it becomes a direct communication channel inside your client’s CMS.
Final thoughts
Rather than removing the Welcome dashboard altogether, extending it allows you to turn an otherwise generic screen into something genuinely valuable for your clients.
With just a small custom service, you can make the Umbraco backoffice feel more tailored, more informative, and more aligned with the people actually using it.

Top comments (1)
This is great stuff Søren! I have the replacing of the default welcome dashboard somewhere on my backlog, but I always assumed I would need to replace the whole thing. Never would expect that is was a service that you can override. Very cool!