I recently published the source code and .NET template package for a website starter kit Iām working on called fr1end1y.
This post gives some background about the project, my goals with it, potential issues and limitations of the starter kit in its current state, and of course, some security considerations.
Contents #
Inspiration #
Lots of folks in the Umbraco community have been experimenting with static site generators, such as Astro and Next.js, and combining these with the Umbraco Delivery API to retrieve content.
Here are but a few great articles:
- A next.js Frontend for Your Umbraco Site by Liam Laverty
- Rebuilding with Astro and Umbraco 13 by Harry Gordon
- Quick nā dirty blog with Astro and Umbraco by Kenn Jacobsen
- Astro-nomically Performant Websites using the Content Delivery API by Louie Richardson
And Iām sure there are more (let me know and I will link them here!).
Eleventy has joined the game š® #
Eleventy is my favourite static site generator and as far as I know nobody has done an Umbraco-Eleventy integration, so this is my attempt.

Project Goals #
Learn stuff! #
As I foray back into the world of software development, Iāve had to get back up to speed with some things and learn how to build applications again (as opposed to just breaking them šØ).
I mainly wanted to get familiar with the new (to me) Delivery API and make use of the latest versions of Umbraco and Eleventy (15 and 3 respectively, at the time of writing).
I also wanted to take things right back to basics and build a website from scratch using plain old HTML and CSS ā no heavy frameworks. More on that later.
Release something #
Anythingā¦ I have so many half-finished (ok ok, barely-started) research projects and blog posts that get left in private GitHub repos to rot. I want to get used to sharing more stuff, even if itās not as polished as I would like.
There are some issues with the starter kit I am aware of, and probably more I am not. As such, it should not be considered stable and will remain on major version zero for now š§Ŗ.
Keep it simple #
This is intended to be a minimal starting point for anyone wanting to use Umbraco and Eleventy together. It is not a complete framework by any means. Users are encouraged to rip it apart, modify it, bring in their own tooling, etc.
Iāve tried to keep the number of direct dependencies and features light so that I might actually be able to keep up with maintenance and upgrades (and Iām already behind š¬). Aside from Umbraco and Eleventy, there are just a few utility libraries/plugins, and the mighty uSync from Kevin Jump.
It really only supports rich text content at the moment. I havenāt looked at doing block lists/grids or anything more complex like that. The initial aim will be to fully support the new Tiptap-based rich text editor.
Write documentation #
As a way of dogfooding the starter kit and getting used to the new rich text editor from a content editorās perspective, I built the documentation site on top of it.
The dotnet new
template actually uses the documentation site as its source, so the documentation site effectively is the starter kit (so meta š).
Support rapid development #
The starter kit is distributed as a .NET template package on NuGet for easy installation. This approach was very much inspired by Dean Leighās excellent UmBootstrap starter kit.
As I mentioned, uSync is included and is configured for FirstBoot so that all content and settings are imported automatically when Umbraco is first launched. Unattended installs are also supported so you can have a fully working site spun up in minutes (at least, that is the aim).
I also wanted to try and reduce some of the āwiring upā and boilerplate code that often needs written to connect a frontend and an API. To that end, Umbraco nodes are automatically rendered to Eleventy Nunjucks templates based on their content type. For example, a node of type webPage
will be rendered with the template _includes/pages/webPage.njk
. All pages inherit a base layout by default.
Within templates, the currently rendering page can be accessed in the currentPage
variable. Umbraco content and settings are also exposed via the umbraco
global data variable (for more details, see the documentation).
A JavaScript Delivery API client (generated with @hey-api/openapi-ts and TypeScript) is included in the project, and there is an npm script to regenerate the client if need be.
Finally, the Eleventy Dev Server is configured to watch the uSync directory for changed files (indicating that either content, media, or settings were changed in Umbraco) and will rebuild and reload the dev site in the browser automatically š.
HTML-first #
The HTML First approach is:
- If you can do it with HTML, use HTML
- If you canāt do it with HTML, use CSS
- If you canāt do it with HTML or CSS, use Javascript
In my opinion, all websites should endeavour to work without CSS and JavaScript. But try disabling CSS and/or JavaScript in your browser and see how many Completely Brokenā¢ websites there are out there (I built some of them myself!).
There are several reasons why CSS or JavaScript might not be available and so a robust website should aim to work with HTML only (in other words, degrade gracefully). Yes, browser default styles might look like crap, but not everyone sees them.
We should care deeply about the HTML we serve up to users, ensuring that it is well-formed and semantic (no div
s pretending to be buttons, no buttons that donāt do anything without JavaScript, etc). In doing so, we make our sites more accessible and ensure a minimum viable experience for everyone.
Iām not a front-end or accessibility expert though, so I wonāt make any grand claims about the accessibility of the starter kit. Iām open to any feedback on how to improve it as its something Iām keen to learn more about!
Progressive enhancement #
Of course, we will want to make our websites look less crap for those who can see them, so we progressively enhance the HTML with CSS.
Like I said, I didnāt want to depend on any big frameworks if I could help it. I initially considered releasing the starter kit without any styling at all, but I bought Andy Bellās excellent Complete CSS course last year and I just had to apply some of what I learned from it.
Iāve opted for a vanilla CSS approach based on the CUBE CSS methodology:
With CUBE CSS, we embrace the cascade and inheritance to style as much as possible at a high level. This means that when nothing but your global styles make it to the browser, the page will still look great. Itās progressive enhancement in action and enables us to write as little CSS as possible.
Sensible global styles have been defined for (almost) all HTML elements and CSS variables are used to configure the overall theme and ensure things like colours, fonts, borders, and spacing are kept consistent across the site.
Some compositions from Every Layout have been brought in, and fluid type/space scales from Utopia (both fantastic resources). Bundling is handled by the Eleventy Bundle plugin so there are no extra dependencies or build steps.
Zero-JavaScript output #
By default, Eleventy does not include any costly runtime JavaScript bundles which can impact site performance.
Iāve also not needed any client-side JavaScript, so there simplyā¦ isnāt any. Less code, better performance, fewer dependencies on frameworks developed by evil corporations, and a whole class of security vulnerabilities avoided. Wild.
Tabs not spaces š± #
Just because I seen Eleventy do this for its 3.0.0 release, I decided to do the same (cue the pitchforks š). Iāve always used spaces to indent my code, but now that I know there are accessibility benefits to using tabs, Iām trying to switch. EditorConfig and Prettier made this change fairly painless.
Limitations and Known Issues #
Like I said, this should be considered an unstable release of the starter kit, my sid if you will, and there are some things to be aware of if you decide to use it.
Dynamic content #
Eleventy produces a truly static site. That is, just flat HTML files. There is no runtime rendering of pages, so we need to think about how to handle when content in the CMS changes and trigger a new Eleventy build to update the site.
Iāve experimented with Umbraco notifications to trigger a local Eleventy build when CMS content is published. There is an example in the project but it is disabled by default.
At some point Iād also like to explore using Umbraco webhooks to trigger Eleventy builds on a build server somewhere. If anyone has done anything like this Iād love to hear about it.
For more interactive, client-side components, thereās the Eleventy <is-land>
plugin which can be used to progressively enhance pages with islands of dynamic/interactive content, but I havenāt experimented with it yet.
Build performance for big sites #
Right now the Eleventy build grabs all Umbraco content at build time (well, really it just sets the API take
parameter to a high value so it might not get āallā nodes if you have a lot). This is fine for my small documentation site, but large sites or sites with lots of media may take longer to build.
I simply havenāt done any testing with big node trees yet. There are definitely some performance optimisations I know of that could be made, but builds have not become slow enough to annoy me yet. The Eleventy Image plugin is included which caches Umbraco media images locally, so that should help speed things up a bit.
Eleventy output site structure reflects Umbraco node tree structure #
There is a direct mapping between the Umbraco node tree structure and the static site directory structure. Editors will see the same URL in the backoffice as they do on the front end, and it gives editors full control over the URLs for SEO purposes.
Some may prefer to do their own routing on the front end application and just use Umbraco as a data source, which is fine, and may even be the preferred approach depending on your application.
However, in this instance I wanted the URLs in the backoffice to match up with the generated site because that makes sense to me at least for websites (as opposed to, say, spas, which is not what this starter kit is for).
Only Nunjucks templates are supported #
At one point during development I got sidetracked experimenting with Eleventyās WebC and even built a version of the starter kit using WebC templates. I thought WebC was cool but ultimately I decided Nunjucks would be more familiar and easier to grasp. I may revisit WebC one day.
Unsupported property editors #
Like I said, Iām only looking to support the rich text editor at the moment, to keep things simple and maintainable. I might look into others in future.
No Umbraco preview #
Preview is not yet implemented, but Iām looking into it.
Unpublished content on first boot #
The content imported by uSync FirstBoot currently needs to be manually published. And sometimes the Delivery API index needs rebuilt after that. Iāve documented this and a bunch of other issues I ran into during development on the troubleshooting docs page.
Deployment & hosting #
I havenāt really explored any advanced deployment and hosting scenarios. The docs site is just pushed to the /docs
folder in the GitHub repo which has GitHub Pages enabled on it.
Refer to the Umbraco and Eleventy docs for information about deployment and hosting options for each.
Security Considerations #
Ok, I suppose I should also say some stuff about security in this blog š.
The Umbraco security docs provide some general guidance on how to deploy Umbraco securely. I wonāt repeat those here. However, the following are some things worth mentioning relevant to the starter kit.
Delivery API configuration and keys #
By default, the starter kit disables PublicAccess
on the API and sets a random GUID as the API key in both the appsettings.json
(in the CMS
project) and .env
(in the Site
project).
Iām not sure how good a GUID is for this purpose, but it was an easy way to generate a random value during the solution creation. It can be changed to any string you want.
Itās important to keep API keys (and other secrets) out of source code repositories, use different keys for different environments, rotate keys occasionally, and store them securely. See the OWASP Secrets Management Cheat Sheet for further guidance.
Delivery API data exposure #
I always recommend keeping sensitive information out of the node tree, more so now because by default the Delivery API, when enabled, will return every property of every content type unless configured not to.
I have seen things like API keys and credentials being stored in Umbraco āSite Settingsā nodes (and Iām sure Iāve done this myself before š), which means they could also be stored in the database, content cache, Examine indexes, and who knows where else.
If you really need to store something in the node tree that you donāt want exposed through the API, consider adding the content type to the DisallowedContentTypeAliases
configuration option and rebuilding the Delivery API index to stop exposing content of that type.
HTML injection attacks coming from the CMS #
Content coming from rich text editors in the CMS is passed through DOMPurify during the Eleventy build to prevent cross-site scripting (XSS) attacks. Of course, the editor in the Umbraco backoffice prevents the injection of scripts to some degree, but this gives some extra protection in the event the underlying HTML is tampered with somehow. Defence in depth and all that.
This could cause issues and strip out tags or attributes that you might want to allow, but exceptions can be allow-listed in the DOMPurify configuration.
Other? #
Iāve probably missed something, but this post has gotten way too long. Issues will be patched accordingly and hopefully blogged about here if they are interesting š.
Contributing #
The starter kit is open source on GitHub and I welcome contributions via issues or pull requests.
If you found this interesting or useful in any way, let me know on Discord (I am stvnhrlnd
) or Mastodon.
āļø