Umbraco 16 brings back segments

The first release candidate for Umbraco 16 dropped yesterday! It contains a boatload of stuff - check the release docs for details 🚀
I’m particularly excited that Umbraco 16 brings back support for segment variation, which has been missing since the introduction of the “new” backoffice in Umbraco 14.
What’s even more exciting is that segments are now closer to being treated as a first class citizen in Umbraco. While they’re not strictly managed like languages, a lot of effort has gone into supporting and future proofing segments.
And the best of it all? It’s way easier than ever to get up and running with segments 🥳
So - what’s the deal?
Generally speaking there are two steps involved to make segments work:
- Enable and define the available segments for content editors.
- Render the segmented content for end users.
Or, in other words: Backoffice and frontend stuff 🤓
The backoffice stuff
Defining the available segments
To define the segments available to the content editors, create an implementation of ISegmentService
:
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
namespace My.Site;
public class MySegmentService : ISegmentService
{
// define a collection of segments to use
private readonly Segment[] _segments =
[
new Segment { Alias = "vip-members", Name = "VIP members" },
new Segment { Alias = "recurring-visitors", Name = "Recurring visitors" }
];
public Task<Attempt<PagedModel<Segment>?, SegmentOperationStatus>> GetPagedSegmentsAsync(int skip = 0, int take = 100)
=> Task.FromResult
(
Attempt.SucceedWithStatus<PagedModel<Segment>?, SegmentOperationStatus>
(
SegmentOperationStatus.Success,
new PagedModel<Segment> { Total = _segments.Length, Items = _segments.Skip(skip).Take(take) }
)
);
}
Enabling segments in the backoffice
Segments are disabled by default. This makes sense because segments aren’t managed by Umbraco, so without a custom ISegmentService
there won’t be any segments.
To enable segments:
- Replace the default
ISegmentService
implementation with the custom one, and - Configure the editor UI to allow segmentation.
This can all be done by means of composing:
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Services;
namespace My.Site;
public class MySegmentComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// register the custom segment service in place of the Umbraco core implementation
builder.Services.AddUnique<ISegmentService, MySegmentService>();
// update segment configuration so segments are enabled (in the client)
builder.Services.Configure<SegmentSettings>(settings => settings.Enabled = true);
}
}
And that’s it - segments can now be enabled for document types:
…and applied to property types:
…and edited in the document editor:
Segments will eventually also be supported by the built-in preview UI - hopefully before Umbraco 16 final is released 🤞
The frontend stuff
Segment editing is in place. Now it’s time to put them to use 🔨
When outputting content that varies - by segment, or by language for that matter - the trick is to tell Umbraco which variant should be rendered for a given request.
Umbraco handles languages out of the box, because languages are managed by Umbraco. They’re usually tied to a hostname or a relative request path, and thus implicitly triggered by the request itself.
Segments are different. Since they’re not managed by Umbraco, “something else” needs to figure out which segment should be active. That “something else” is usually Umbraco Engage - but you can roll your own, if you care to invest the hours 🫠
Templated rendering of segmented content
With templated (Razor) rendering, all core Umbraco features services are immediately available. This includes the VariationContext
, which exists to contextualize variance towards Umbraco.
VariationContext
is request scoped, and can be accessed from anywhere using the injectable IVariationContextAccessor
.
Here’s a basic example of a Razor template that sets the active segment based on a query string parameter:
@inject IVariationContextAccessor VariationContextAccessor
@using Umbraco.Cms.Web.UI.Custom
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@{
Layout = null;
var segment = Context.Request.Query["segment"].ToString();
if (segment.IsNullOrWhiteSpace() is false)
{
VariationContextAccessor.VariationContext = new VariationContext
(
VariationContextAccessor.VariationContext?.Culture,
segment
);
}
}
<html lang="en">
[do your thing here]
</html>
Requesting a page using this template with ?segment=vip-members
will trigger a rendering for the “VIP Members” segment:
Delivery API support for segmented content
Umbraco 16 has built-in support for fetching segmented content through the Delivery API 🤘
The assumption is that segmentation somehow happens on the consumer (client) side, which means the active segment should be known up front when fetching from the Delivery API.
To request a segment, simply specify it in the newly introduced Accept-Segment
header - for example, Accept-Segment: vip-members
:
So… segments?
There’s no denying that Umbraco 16 makes it easier than ever to get segments working from scratch. And while they still feel a little less like a first class citizen than languages do, Umbraco 16 is definitively a great step towards fully embracing segments.
The more interesting question is: Do they make any sense without Umbraco Engage 🤔
I suppose it all boils down to your concrete requirements, and your willingness to invest into building and maintaining custom personalization logic. Segments can probably work fine on their own if:
- Your requirements are simple, clearly defined and easy to test.
- You already have an external personalization engine running, and it can provide some indication of “active user segment”.
I, for one, am definitively going to explore segments some more. Expect more posts on this subject in the immediate future 😉
Happy segmenting 💜