Keep Umbraco Simple

Per Ploug's talk at the London Umbraco festival last year, on keeping Umbraco simple, made me wonder just how simple it could get. Could you take it to the extreme, and build a useable website in Umbraco with as little code and as few dependencies as possible?

Umbraco is powerful, and you can make it do a lot of fancy stuff. But what if you're new to building websites, or coming from a different skillset? Maybe you're deciding whether to use Umbraco rather than something like (cover your ears) Wordpress. All that funky .Net extensibility might make you think twice about joining the Umbraco party. As Per said, there's nothing wrong with complexity, but you should be able to put it aside until you need it.

keep-umbraco-simple.jpg

Let's get simple, really simple. Simpler than a very simple thing, simpler than a Trump love child who didn't keep up at school. Using nothing but vanilla Umbraco, out of the box. There isn't even a box. 

So, for this example, no packages, not even the most excellent content editors like Nested Content, Archetype, nuPickers, or the Umbraco grid. No hijacked routes, no models builder, no nuget. Believe me folks. No nuget. You're the nuget. 

BUILD A GREAT WEBSITE, VERY INEXPENSIVELY

That traditional approach of using a different template for every type of page can create a maintenance nightmare, with small changes to layout leading to duplicated code and confused editors. Coping with change is best done with a modular layout, and the more generic the better, so you create document types for each type of content module and allow the modules to placed anywhere on the page. Layout is determined inside the content tree rather than in a template. 

Here, we'll cut it down to the least code possible, while still being flexible enough to create a useable website. 

EXECUTIVE ORDERS (MAY BE OVERTURNED BY FEDERAL JUDGES)

For simplicity and readability, Razor script only, no compiled code. 

No templates apart from a master layout to hold the header, navigation and footer, and a single page template.

No custom data types.

Document types built with compositions, so we can share properties like title and body text.

All navigable pages to be the same document type - called Page - to make building the menus really easy.

Generic nestable modules like section, block, collection, item, etc.

Each document type, including Page, to be rendered by a single associated partial view, with the same name. So a section for example will be rendered by Section.cshtml.

And a simple bit of code that knits it all together, recursively. Let's start putting it all together.

GRAB 'EM BY THE C#

Firstly, create a doc type composition called Main to hold properties that will be common to a lot of the content types - image, title, intro paragraph and body text.

Add some document types:

  • Page - all navigable pages will use this doc type
  • Section - spans the full width of the page and contains other content items
  • Block - a cell within a section spanning a specified number of columns
  • Collection - a list of other items that may be sorted, paginated, etc
  • Article - a basic list item, that can be extended for particular duties such as a news item

Create a stripped-down Master layout. I'm using a custom version of Bootstrap for this example:

Layout.cshtml

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{ Layout = null;
IPublishedContent root = CurrentPage.AncestorOrSelf(1);
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="/favicon.ico">
<title>@Model.Content.Name : Simolismo</title>
<!-- Bootstrap -->
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/styles.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body class="@Model.Content.Name.ToSafeAlias(true)">
@Html.Partial("Header", root)
@RenderBody()
@Html.Partial("Footer", root)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>

The RenderBody() call renders the inherited content, which is our Page template:

Page.cshtml

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
Layout = "Master.cshtml";
IPublishedContent mdl = Model.Content;

string title = mdl.GetPropertyValue<string>("title");
int imageId = mdl.GetPropertyValue<int>("image");
string intro = mdl.GetPropertyValue<string>("intro");
string bodyText = mdl.GetPropertyValue<string>("bodyText");

IPublishedContent image = Umbraco.TypedMedia(imageId);
}
<div class="page">
<div class="page_inner container @("n_" + mdl.Name.ToSafeAlias(true))">
@if (!string.IsNullOrEmpty(title))
{
<h1 class="page_title">@title</h1>
}
@if (!string.IsNullOrEmpty(intro))
{
<p class="page_intro">@Html.Raw(intro)</p>
}
@if (image != null)
{
<img src="@image.Url" alt="@image.Name" class="page_image img-responsive" />
}
@if (!string.IsNullOrEmpty(bodyText))
{
<div class="page_content">
@Html.Raw(@bodyText)
</div>
}
</div>

@if (mdl.Children.Any())
{
@Html.Partial("BodyContent", mdl)
}

</div>

Html.Partial("BodyContent") renders any child nodes. You could of course use CachedPartial to make it faster.

BodyContent.cshtml

@inherits Umbraco.Web.Mvc.UmbracoViewPage<IPublishedContent>
@{ 
IEnumerable<IPublishedContent> selection = Model.Children.Where(c => c.DocumentTypeAlias!="page");
bool equalBlockHeight = Model.DocumentTypeAlias == "section" && Model.Children.All(c => c.DocumentTypeAlias == "block") && (Model.Children.Sum(c => c.GetPropertyValue<int>("columns")) <= 12);
}
@if (selection.Any())
{
<div class="@(Model.DocumentTypeAlias.ToLower() + "_children" + (equalBlockHeight ? " row row-eq-height": ""))">
@foreach (IPublishedContent item in selection)
{
ViewDataDictionary vdata = new ViewDataDictionary();
Html.RenderPartial(item.DocumentTypeAlias, item, vdata);
}
</div>
}

The corresponding partial view for each document type might look like this one, for a Section:

Section.cshtml

@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
IPublishedContent mdl = Model.Content;

string title = mdl.GetPropertyValue<string>("title");
int imageId = mdl.GetPropertyValue<int>("image");
string intro = mdl.GetPropertyValue<string>("intro");
string bodyText = mdl.GetPropertyValue<string>("bodyText");

IPublishedContent image = Umbraco.TypedMedia(imageId);
}
<section class="container-fluid @("n_" + mdl.Name.ToSafeAlias(true))">
<div class="container">
<div class="section_inner">
@if (!string.IsNullOrEmpty(title))
{
<h2 class="section_title">@title</h2>
}
@if (!string.IsNullOrEmpty(intro))
{
<p class="section_intro">@Html.Raw(intro)</p>
}
@if (image != null)
{
<img src="@image.Url" alt="@image.Name" class="section_image img-responsive" />
}
@if (!string.IsNullOrEmpty(bodyText))
{
<div class="section_content">
@Html.Raw(@bodyText)
</div>
}
</div>
@if (mdl.Children.Any())
{
@Html.Partial("BodyContent", mdl)
}
</div>
</section>

The block, collection and article partial views will be very similar to that, and all will basically render the item content, then call the BodyContent macro partial, which renders each child item in turn, and in the process render any child items of theirs. Thus we render the entire page recursively.

NOT FAKE NEWS, FOLKS

Have a look at an example of a website built like this. Apart from Umbraco all I've added are Bootstrap and jQuery, to handle the menus, grid layout and widgets such as sliders, tabs and accordions.

simplismo-website.png

The total file size of the views in this example comes to less than 35Kb. Can you beat that?

In real life of course, things are not so simple. Although a lot of the common components of a website are covered, any real-life project will need additional features and functionality. But I've built a number of commercial sites using an extended version of this model, and my experience is that it works equally well for sites large or small, simple or complex, bootstrap or whatever, quick-and-easy or big budget. You can extend it using whatever techniques you like.

Here are a few basic tips for making this work in real life.

  • Put as much code as possible into compiled objects. Razor script is easy to read and debug and great for an example like this, but come on, let's be professional.
  • Build templates based on Page for different page layouts like Page with Side Menu, or Page with Three Columns. 
  • Use additional templates only for the reason that they were intended, i.e. where you have a lot of similar pages with the same complex structure.
  • Store the root node in a TempData variable to avoid having to access it more than once to retrieve settings, etc.
  • Use the excellent ResponsiveBP framework by Umbraco image maestro James South, setting the appropriate column classes on blocks and collection items.
  • Use Html.CachedPartial instead of Html.Partial to speed things up, except for content displayed according to values in the query string.
  • Add a content chooser module that allows users to reuse content from elsewhere in the tree, to display for example, three items of latest news on the home page.
  • For search, aggregate results by their Page ancestor and display one row per page.  

By the way, you can offer different views for a particular document type by setting the template on content items in the tree. All you have to do is check that the TemplateId is not zero, then render the TemplateAlias view rather than DocumentTypeAlias.

IT'S GOING TO BE A FANTASTIC WEBSITE, BELIEVE ME

For the user, having content layout defined in the content tree is highly intuitive. It's very easy to find the content you want to edit. Editors have freedom to put content where they like, and have access to all the standard Umbraco functions, like Copy, Move, Sort and Rollback.

The advantages to the developer are speed and the ability to adapt quickly to changing designs. You can modify content layout very quickly, without the need to code, because everything is held in the tree. Back-end development time can be cut by as much as 80%. Yes, honestly!

There are downsides of course, but experience tells me that they can be overcome or are heavily outweighed by the advantages. Over the past couple of years, Pixel<to>Code built some fast, responsive sites that users find easy to edit. Our agency clients love the ability to respond to customer requests quickly and cheaply, and end users pick up on how to edit content almost immediately.

Are you doing something like this already, or interested in giving it a try? All feedback is welcome, so please contribute to the discussion by commenting below.

Design, and we shall build

Fast, reliable, experienced ASP.Net Umbraco developers

Front-end, back-end, testing, deployment... Outsource your web development projects to the CMS experts.

Contact us

We can't wait to hear from you

More articles

Developing matters