Starting the Project - FadiZahhar/umbraco8showandtell GitHub Wiki
-
Purpose: Store content and configuration that is relevant across the whole site, not just for one page.
-
Examples:
-
Site logo
-
Social media links
-
Footer content
-
Contact details
-
SEO metadata defaults
-
Google Analytics scripts
-
-
Editors can update key settings (e.g., change the logo or contact info) without developer intervention or having to hunt through templates or config files.
-
All global settings are easily accessible in one place in the backoffice.
-
Keeps content separate from configuration: Page content lives in content nodes; site-wide settings live in the settings node.
-
Avoids hard-coding values or spreading “global” content across multiple templates or pages.
-
Centralized updates: Change once, reflected everywhere.
-
Less risk of mistakes or inconsistencies (e.g., multiple versions of a phone number).
-
When you add new global features, you have a clear place to manage them.
-
Templates and partial views can reference settings from a single location (e.g.,
Model.Root().DescendantOrSelf("siteSettings")
). -
If you ever run a multi-site install, you can set up one settings node per site.
-
Easier to export/import global settings during migration.
-
Clean separation between dynamic content and static/global configuration.
-
Create a Site Settings Document Type (often with an icon like a gear or cog).
-
Add relevant properties (logo, company info, etc.).
-
Place a single Site Settings node under the root of your Content tree (often hidden from navigation).
-
Reference these settings in your Razor templates (header, footer, etc.).
Content
├── Home
├── About Us
├── Contact
├── Site Settings <-- Not a page, but a node for global settings
Benefit | Description |
---|---|
Centralized management | One place for all global settings |
Editor empowerment | No need for developer for simple global changes |
Clean templates | No hard-coded or repeated values |
Future-proofing | Easy to add more settings as your site grows |
Consistency | Prevents inconsistencies across the site |
-
Go to: Settings > Document Types
Click Create Document Type. -
Name:
Site Settings
-
Icon:
Choose something like a gear ⚙️ or a toolbox. -
Permissions:
-
Allow as root: Yes
-
Can be composed into other types: No
-
-
Add Properties (Tabs/Groups & Fields):
General Tab:
-
Logo (Type: Media Picker, Alias:
logo
) -
Site Name (Type: Textstring, Alias:
siteName
)
Social Tab:
-
Facebook URL (Type: Textstring, Alias:
facebookUrl
) -
Instagram URL (Type: Textstring, Alias:
instagramUrl
) -
Twitter URL (Type: Textstring, Alias:
twitterUrl
)
-
Compositions in Umbraco 8 are a way to create reusable sets of fields (properties) that can be “mixed in” to multiple Document Types—kind of like adding “traits” or “features” to different content blueprints.
-
Normal Document Types:
Define the fields for a single content type (e.g., Blog Post, Product, Content Page). -
Composition Document Types:
Define a set of fields (like a “component” or “mixin”) that can be reused by multiple Document Types.
-
DRY Principle: Avoid repeating the same fields across multiple Document Types.
-
Consistency: Ensure fields like SEO, Hero Section, or Metadata are always the same everywhere they’re used.
-
Maintainability: Update the composition in one place, and all Document Types using it get the change.
Let’s say you want every page on your site to have SEO fields (Meta Title, Meta Description, Open Graph Image).
Instead of adding these fields manually to every Document Type (Home, About, Blog, etc.), you:
-
Create a “SEO” Composition Document Type
-
Add fields: Meta Title, Meta Description, OG Image.
-
-
Attach the SEO Composition to any Document Type that needs SEO settings.
Now, each page that uses this composition automatically gets the SEO fields—no copy-pasting, no extra work.
-
Create a New Document Type (Composition):
-
Go to Settings > Document Types.
-
Click “Create new Document Type.”
-
Name: “SEO” (or “Hero Section”, “Metadata”, etc.)
-
Set “Allow as root” to No (this keeps it out of the content tree—it’s just for composing).
-
Add your reusable fields (properties).
-
-
Add the Composition to Another Document Type:
-
Edit any Document Type (e.g., Blog Post, Content Page).
-
Go to the Compositions tab.
-
Select your SEO Composition.
-
Save.
Now, this Document Type will include all the fields from the SEO Composition!
-
-
Compositions are like Lego bricks:
-
Build a brick (e.g., SEO fields).
-
Attach that brick to any model (Document Type) you want.
-
You can reuse the same brick in lots of places—just “click it in.”
-
-
SEO properties (meta fields, OG image)
-
Hero sections (title, subtitle, background image)
-
Common settings (visibility, publish date, tags)
-
Social media fields
Term | Used For | Shows in Content Tree? | Typical Example |
---|---|---|---|
Document Type | Pages, blocks, main content | Yes | Blog Post, Product |
Composition | Reusable field sets, shared by many types | No | SEO, Hero Section |
-
Compositions = reusable field groups for Document Types.
-
Keeps your site DRY, organized, and maintainable.
-
Update in one place; affect many types.
sion
-
Go to: Content
-
Create a new node at the root, choose Site Settings.
-
Fill in the logo and social links.
To prevent editors from accidentally publishing the Site Settings as a page:
-
Set permissions so only admins can access/edit.
-
(Optional) Use a property like “Hide from Navigation” if your navigation renders all root nodes by default.
Here’s how you fetch and use Site Settings values in your views (e.g., header, footer).
@{
// Get the root node (homepage or site root)
var root = Model.Root();
// Find the Site Settings node by document type alias
var siteSettings = root.DescendantsOrSelf("siteSettings").FirstOrDefault();
}
@if (siteSettings != null && siteSettings.HasValue("logo"))
{
var logoMedia = siteSettings.Value<IPublishedContent>("logo");
<img src="@logoMedia?.Url" alt="Site Logo" />
}
@if (siteSettings != null)
{
<ul class="social-links">
@if (!string.IsNullOrEmpty(siteSettings.Value<string>("facebookUrl")))
{
<li><a href="@siteSettings.Value<string>("facebookUrl")" target="_blank">Facebook</a></li>
}
@if (!string.IsNullOrEmpty(siteSettings.Value<string>("instagramUrl")))
{
<li><a href="@siteSettings.Value<string>("instagramUrl")" target="_blank">Instagram</a></li>
}
@if (!string.IsNullOrEmpty(siteSettings.Value<string>("twitterUrl")))
{
<li><a href="@siteSettings.Value<string>("twitterUrl")" target="_blank">Twitter</a></li>
}
</ul>
}
Property | Type | Alias | Example Value |
---|---|---|---|
Logo | Media Picker | logo | /media/1234/logo.png |
Site Name | Textstring | siteName | My Awesome Site |
Test Mode | boolean | Test mode state | true |
Facebook URL | Textstring | facebookUrl | https://facebook.com/mysite |
Instagram URL | Textstring | instagramUrl | https://instagram.com/mysite |
Twitter URL | Textstring | twitterUrl | https://twitter.com/mysite |
Your Views Folder Structure should be as following Views
folder:
Views/
About.cshtml
Home.cshtml
main.cshtml
Web.config
/Partials
NavBar.cshtml
Footer.cshtml
/Grid
Bootstrap3.cshtml
Bootstrap3-Fluid.cshtml
/Editors
Base.cshtml
Embed.cshtml
Macro.cshtml
Media.cshtml
Rte.cshtml
Textstring.cshtml
You’re using:
-
A main layout file: main.cshtml
-
Partials: NavBar.cshtml, Footer.cshtml
-
Grid/Editors: for advanced content—these are fine as-is for the layout tutorial
Here’s a step-by-step guide customized to your actual folder and file names.
File: Views/main.cshtml
This is your _Layout.cshtml
equivalent!
Add these lines to the <head>
section of main.cshtml
:
<!-- Bootstrap 4 CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap" rel="stylesheet" />
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
Just before </body>
, add:
<!-- Scripts: jQuery, Popper, Bootstrap -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
// Cookie Consent Logic
$(document).ready(function () {
if (!localStorage.getItem('cookieConsentAccepted')) {
$('#cookieConsent').fadeIn();
}
$('#acceptCookies').on('click', function () {
localStorage.setItem('cookieConsentAccepted', 'true');
$('#cookieConsent').fadeOut();
});
});
</script>
In main.cshtml
, replace any static navigation/footer HTML with calls to your partials:
@Html.Partial("Partials/NavBar")
...
@RenderBody()
...
@Html.Partial("Partials/Footer")
...
@Html.Partial("Partials/CookieConsent") <!-- (You'll need to add this file, see below) -->
File: Views/Partials/CookieConsent.cshtml
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<div class="cookie-consent" id="cookieConsent" style="display:none;position:fixed;bottom:0;width:100%;background:#333;color:#fff;padding:1rem;z-index:1000;">
This website uses cookies to ensure you get the best experience.
<button class="btn btn-primary btn-sm" id="acceptCookies">Accept</button>
</div>
In each template (e.g., Views/Home.cshtml
, Views/About.cshtml
), set:
@{
Layout = "main.cshtml";
}
Write your page-specific content below that.
Enhance if needed to loop through your root nodes, for example:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">@Umbraco.Field("siteName", recursive: true)</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
@{
var root = Umbraco.ContentAtRoot().FirstOrDefault();
if (root != null)
{
foreach (var page in root.Children().Where(x => x.IsVisible()))
{
<li class="nav-item">
<a class="nav-link" href="@page.Url">@page.Name</a>
</li>
}
}
}
</ul>
</div>
</nav>
Enhance as needed; for example:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<footer class="bg-dark text-light text-center py-4 mt-5">
<div>
<span>© @DateTime.Now.Year @Umbraco.Field("siteName", recursive: true) | All rights reserved.</span>
</div>
<div>
<a href="#" class="text-light mx-2"><i class="fab fa-facebook"></i></a>
<a href="#" class="text-light mx-2"><i class="fab fa-instagram"></i></a>
<a href="#" class="text-light mx-2"><i class="fab fa-twitter"></i></a>
</div>
</footer>
You can keep your CSS in the <style>
tag in main.cshtml
or add a custom CSS file and link it in the head.
File Location | Purpose |
---|---|
Views/main.cshtml | Main layout (“master page”) |
Views/Partials/NavBar.cshtml | Navigation bar partial |
Views/Partials/Footer.cshtml | Footer partial |
Views/Partials/CookieConsent.cshtml | Cookie consent popup partial |
Views/Home.cshtml, etc. | Actual page templates |
-
Edit
main.cshtml
:-
Add the library references in
<head>
and before</body>
. -
Insert
@Html.Partial
for NavBar, Footer, and CookieConsent in the correct spots.
-
-
Create
CookieConsent.cshtml
in/Views/Partials/
with the code above. -
Check NavBar and Footer:
-
If needed, update their code per examples to support Bootstrap and dynamic site data.
-
-
Set the Layout in all page templates (
Home.cshtml
,About.cshtml
, etc.):@{ Layout = "main.cshtml"; }
-
Test the site to make sure the layout, navigation, footer, and cookie consent show as expected.
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>
@if (Umbraco.Field("title", recursive: true).HasValue())
{
@Umbraco.Field("title", recursive: true)
}
else
{
@Umbraco.Field("siteName", recursive: true)
}
</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- Bootstrap 4 CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" />
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap" rel="stylesheet" />
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
<style>
body { font-family: 'Roboto', Arial, sans-serif; }
.cookie-consent { position: fixed; bottom: 0; width: 100%; background: #333; color: #fff; padding: 1rem; display: none; z-index: 1000; }
.cookie-consent button { margin-left: 1rem; }
</style>
@RenderSection("Head", required: false)
</head>
<body>
<!-- NAVIGATION -->
@Html.Partial("Partials/NavBar")
<!-- PAGE CONTENT -->
<main class="container my-4">
@RenderBody()
</main>
<!-- FOOTER -->
@Html.Partial("Partials/Footer")
<!-- COOKIE CONSENT -->
@Html.Partial("Partials/CookieConsent")
<!-- SCRIPTS -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>
// Cookie Consent Logic
$(document).ready(function () {
if (!localStorage.getItem('cookieConsentAccepted')) {
$('#cookieConsent').fadeIn();
}
$('#acceptCookies').on('click', function () {
localStorage.setItem('cookieConsentAccepted', 'true');
$('#cookieConsent').fadeOut();
});
});
</script>
@RenderSection("Scripts", required: false)
</body>
</html>
Create this file if it does not exist:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<div class="cookie-consent" id="cookieConsent">
This website uses cookies to ensure you get the best experience.
<button class="btn btn-primary btn-sm" id="acceptCookies">Accept</button>
</div>
At the top of each of your page templates (e.g., Home.cshtml
, About.cshtml
):
@{
Layout = "main.cshtml";
}
-
The partials already exist:
/Views/Partials/NavBar.cshtml
/Views/Partials/Footer.cshtml
-
Make sure they use Bootstrap classes and pull dynamic data as needed (see earlier examples for dynamic navigation and footer).
-
Start your site.
-
You should see:
- Bootstrap/Font Awesome styling
- Your navigation and footer
- Page content in the main container
- Cookie consent banner on first visit (should disappear on click and not return after reload)
- Uses Bootstrap 4 markup and classes.
- Loops through children of the root node to build top-level navigation.
- Hides nodes not meant for navigation (
IsVisible()
). - Highlights the current page (optional, but a nice touch).
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">
@Umbraco.Field("siteName", recursive: true)
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
@{
var root = Umbraco.ContentAtRoot().FirstOrDefault();
if (root != null)
{
foreach (var page in root.Children().Where(x => x.IsVisible()))
{
var isActive = page.Id == Model.Id ? "active" : "";
<li class="nav-item @isActive">
<a class="nav-link" href="@page.Url">@page.Name</a>
</li>
}
}
}
</ul>
</div>
</nav>
What this does:
- Gets the root node (your homepage or site root).
- Loops through all visible children (i.e., pages set to show in navigation).
- Renders each as a Bootstrap navigation item.
- Adds the
active
class if the item matches the current page.
- Uses Bootstrap for layout.
- Dynamically displays the site name and current year.
- Optionally links to social networks—ideally, these would be pulled from Site Settings (see earlier tutorial), but can be hard-coded as placeholders.
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<footer class="bg-dark text-light text-center py-4 mt-5">
<div>
<span>© @DateTime.Now.Year @Umbraco.Field("siteName", recursive: true) | All rights reserved.</span>
</div>
<div>
<a href="#" class="text-light mx-2"><i class="fab fa-facebook"></i></a>
<a href="#" class="text-light mx-2"><i class="fab fa-instagram"></i></a>
<a href="#" class="text-light mx-2"><i class="fab fa-twitter"></i></a>
</div>
</footer>
-
If your content tree is:
Content ├─ Home ├─ About ├─ Contact ├─ (others)
All pages under root (and set to “Show in navigation”) will automatically appear in the navigation bar.
-
The site name will always pull from the root node (ensure your Home/root node has the
siteName
property).
- Add pages in the Content tree, set “Show in navigation.”
- Reload the site—they should appear in the navigation, styled by Bootstrap.
- Footer always shows the current year and site name.
- Mobile? Resize your window—the Bootstrap nav should collapse into a hamburger menu.
Update the footer to pull from your Site Settings node, like this (if you set those up):
@{
var root = Umbraco.ContentAtRoot().FirstOrDefault();
var siteSettings = root?.DescendantsOrSelf("siteSettings").FirstOrDefault();
}
<footer class="bg-dark text-light text-center py-4 mt-5">
<div>
<span>© @DateTime.Now.Year @Umbraco.Field("siteName", recursive: true) | All rights reserved.</span>
</div>
<div>
@if (siteSettings != null)
{
if (!string.IsNullOrWhiteSpace(siteSettings.Value<string>("facebookUrl")))
{
<a href="@siteSettings.Value<string>("facebookUrl")" class="text-light mx-2" target="_blank"><i class="fab fa-facebook"></i></a>
}
// Repeat for instagramUrl, twitterUrl, etc.
}
</div>
</footer>
(Replace "siteSettings"
, "facebookUrl"
etc. with your actual Document Type and field aliases)
- Your current partials do adapt to your content tree if they use the above Razor code.
- Bootstrap 4 ensures navigation/footer are responsive and styled.
- The footer and navigation update automatically as you add, remove, or rename nodes in the backoffice.
-
You have a Site Settings Document Type (alias:
siteSettings
) in your Content tree. -
It has fields like:
facebookUrl
instagramUrl
twitterUrl
-
The settings node is typically under the root of your Content tree.
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
var root = Umbraco.ContentAtRoot().FirstOrDefault();
var siteSettings = root?.DescendantsOrSelf("siteSettings").FirstOrDefault();
}
<footer class="bg-dark text-light text-center py-4 mt-5">
<div>
<span>© @DateTime.Now.Year @Umbraco.Field("siteName", recursive: true) | All rights reserved.</span>
</div>
<div>
@if (siteSettings != null)
{
if (!string.IsNullOrWhiteSpace(siteSettings.Value<string>("facebookUrl")))
{
<a href="@siteSettings.Value<string>("facebookUrl")" class="text-light mx-2" target="_blank" rel="noopener">
<i class="fab fa-facebook"></i>
</a>
}
if (!string.IsNullOrWhiteSpace(siteSettings.Value<string>("instagramUrl")))
{
<a href="@siteSettings.Value<string>("instagramUrl")" class="text-light mx-2" target="_blank" rel="noopener">
<i class="fab fa-instagram"></i>
</a>
}
if (!string.IsNullOrWhiteSpace(siteSettings.Value<string>("twitterUrl")))
{
<a href="@siteSettings.Value<string>("twitterUrl")" class="text-light mx-2" target="_blank" rel="noopener">
<i class="fab fa-twitter"></i>
</a>
}
}
</div>
</footer>
-
Finds the Site Settings node (
siteSettings
), starting from the root. -
Checks if each social link is present (not empty or null).
-
Outputs a link/icon only if the value exists.
-
Best practice:
- Uses
target="_blank"
andrel="noopener"
for security when opening new tabs.
- Uses
- Go to your Site Settings node.
- Enter your Facebook, Instagram, Twitter URLs.
- If a field is empty, its icon won’t appear in the footer.
-
In a standard Umbraco Razor view, the
@Model
variable represents the current content node being rendered. -
By default,
@Model
is of typeUmbraco.Web.Models.RenderModel
. -
The actual content data is stored in
@Model.Content
, which is of typeIPublishedContent
.
@inherits Umbraco.Web.Mvc.UmbracoViewPage
@{
var pageTitle = Model.Content.GetPropertyValue("title");
}
<h1>@pageTitle</h1>
or using the shortcut:
@Umbraco.Field("title")
Or:
@Model.Content.GetValue<string>("title")
-
Dynamic API (
GetPropertyValue
,GetValue
,Umbraco.Field
) is flexible, but:-
Error-prone: Property names are strings (easy to mistype).
-
No compile-time checking or IntelliSense.
-
Refactoring is harder (renaming fields won’t break code until runtime).
-
Umbraco Models Builder is a code generation tool that turns your document types into C# classes.
With it enabled, you get strongly-typed models for your content nodes!
-
For every Document Type you define in Umbraco, Models Builder generates a class (e.g., if you have a document type called "HomePage", you get a
HomePage
class). -
Each property on your Document Type becomes a property in the class (e.g.,
public string Title { get; }
).
-
IntelliSense and compile-time checking in Razor views.
-
Easy refactoring.
-
Clearer, cleaner code:
@Model.Title
vs.@Model.GetValue<string>("title")
-
Enable Models Builder (default in Umbraco 8):
-
It will generate models in
/App_Data/Models/
by default. -
You can also configure it to generate models in a different folder, or use API-only mode.
-
-
Change Your View to Use the Model:
-
Instead of inheriting from the generic
UmbracoViewPage
, inherit fromUmbracoViewPage<HomePage>
(if your document type isHomePage
).
@inherits Umbraco.Web.Mvc.UmbracoViewPage<YourNamespace.Models.HomePage>
<h1>@Model.Title</h1> <p>@Model.ContentText</p>
-
Here,
@Model.ContentText
is a strongly-typed property, representing the "contentText" property on your "HomePage" document type. -
If you have a property named "contentText" in the backoffice, Models Builder creates:
public string ContentText { get; }
-
Access Pattern | Example | Compile-Time Safety? | IntelliSense? | Recommended? |
---|---|---|---|---|
Dynamic (string key) | @Model.Content.GetValue("foo") | ❌ | ❌ | For quick tests |
Umbraco.Field shortcut | @Umbraco.Field("foo") | ❌ | ❌ | For simple cases |
Strongly Typed (Models Builder) | @Model.Foo | ✅ | ✅ | ✅ |
-
Models Builder generates C# classes for your content types for strongly-typed, safer, and easier-to-maintain code.
-
Use
@Model.PropertyName
instead of@Model.GetValue<string>("propertyName")
whenever possible. -
@Model.ContentText
is available if your Document Type has a property calledcontentText
and you use the strongly-typed model in your view.
In Umbraco, navigation menus are usually based on the root node’s children—the main pages at the top of your Content tree.
Example Content tree:
Content
├─ Home (root)
├─ About
├─ Services
├─ Contact
├─ ... (others)
Only the pages set to “Show in navigation” (via IsVisible()
) will appear in the navbar.
Location:
/Views/Partials/NavBar.cshtml
Paste or update the following code:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">
@Umbraco.Field("siteName", recursive: true)
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
@{
// Get the root node (e.g., Home)
var root = Umbraco.ContentAtRoot().FirstOrDefault();
if (root != null)
{
// Loop through each visible child (top-level nav items)
foreach (var page in root.Children().Where(x => x.IsVisible()))
{
// Add "active" class if this is the current page
var isActive = (page.Id == Model.Id) ? "active" : "";
<li class="nav-item @isActive">
<a class="nav-link" href="@page.Url">@page.Name</a>
</li>
}
}
}
</ul>
</div>
</nav>
-
Umbraco.ContentAtRoot().FirstOrDefault()
: Gets the root of your site (usually "Home"). -
.Children().Where(x => x.IsVisible())
: Loops only through pages set to show in navigation. -
page.Id == Model.Id
: Highlights the current page as active for user feedback. -
@Umbraco.Field("siteName", recursive: true)
: Uses your global Site Name for the brand link.
In your /Views/main.cshtml
(master layout), include the navbar like this:
@Html.Partial("Partials/NavBar")
For most sites, top-level navigation is enough. If you want dropdowns for child pages, add inside the loop:
@if (page.Children().Any(x => x.IsVisible()))
{
// Render dropdown menu
}
But keep it simple at first—expand later if you have a lot of subpages.
-
Just add, rename, or re-order pages in Umbraco’s Content section.
-
Set “Show in navigation” for any page you want in the menu.
-
The navigation updates automatically—no code changes needed.
Step | File | Action |
---|---|---|
Dynamic Nav | /Views/Partials/NavBar.cshtml | Use code above for dynamic nav |
Add to Layout | /Views/main.cshtml | @Html.Partial("Partials/NavBar") |
Control Nav | Umbraco Content section (backoffice) | Use “Show in navigation” toggle |
-
Responsive Bootstrap navbar
-
Reflects real-time Content tree
-
Highlights current page
-
Zero hard-coded links!
Location:
/Views/Partials/CookieConsent.cshtml
Code:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
<div class="cookie-consent" id="cookieConsent" style="display:none;position:fixed;bottom:0;width:100%;background:#333;color:#fff;padding:1rem;z-index:1000;">
This website uses cookies to ensure you get the best experience.
<button class="btn btn-primary btn-sm" id="acceptCookies">Accept</button>
</div>
Location suggestion:
/wwwroot/js/CookieConsent.js
(or wherever you keep your static files; for Umbraco 8, ~/js/
or /Scripts/
is common)
Code:
// /js/CookieConsent.js
document.addEventListener("DOMContentLoaded", function () {
// Only show if not previously accepted
if (!localStorage.getItem('cookieConsentAccepted')) {
var consent = document.getElementById('cookieConsent');
if (consent) consent.style.display = "block";
}
var acceptBtn = document.getElementById('acceptCookies');
if (acceptBtn) {
acceptBtn.addEventListener('click', function () {
localStorage.setItem('cookieConsentAccepted', 'true');
var consent = document.getElementById('cookieConsent');
if (consent) consent.style.display = "none";
});
}
});
In /Views/main.cshtml
:
-
Add the partial where you want the popup (usually before
</body>
):@Html.Partial("Partials/CookieConsent")
-
Add the script tag after jQuery/Bootstrap and before
</body>
:<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <script src="~/js/CookieConsent.js"></script>
(Adjust the path to where you put the JS file.)
-
The cookie consent bar appears at the bottom of the page only if the user hasn’t accepted before.
-
Clicking Accept stores the flag in
localStorage
and hides the bar. -
The logic is now modular and can be maintained/extended easily (translations, expiry, etc).
Step | File/Location | What to Do |
---|---|---|
Partial | /Views/Partials/CookieConsent.cshtml | Create the HTML structure |
JS File | /js/CookieConsent.js | Add JS logic for showing/hiding on user action |
Reference | /Views/main.cshtml | Add @Html.Partial and <script src=...> tags |
-
For full compliance (GDPR, etc.), you may want to link a “Learn more” to your privacy policy, or use a cookie management library in production.