Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at

  • Stephen 21 posts 143 karma points
    Mar 14, 2022 @ 23:11

    Multisite Umbraco 9 solution with different front end themes

    I'm building a multisite Umbraco solution where each site shares the same document, data, and media types, but has a very different front-end theme including views, scripts and styles.

    I've so far organised the views and front-end assets into folders, set the theme on a per-site basis via a property in the Umbraco back office, then used a custom render controller to look up the selected theme and load the themed root-level template view.

    i.e.: If the current theme is ThemeXYZ, render the page using ~/Themes/ThemeXYZ/Views/Home.cshtml.

    All subsequent assets and partials loaded are then specific to their theme:

    @await Html.PartialAsync("~/Themes/ThemeXYZ/Views/Partials/BlockList/Components.cshtml", Model.Components)

    The downside of this approach is that every theme must contain files for every view, even though some views are not unique.

    I'd like to remove the need for duplicate code by falling back to a "Default" theme in the event that there is no view for the current theme.

    i.e.: If the current theme is "ThemeXYZ" and ~/Themes/ThemeXYZ/Views/Home.cshtml does not exist, render the page using ~/Views/Home.cshtml instead.

    I've looked into adding to the Razor view location expander, but am finding that Umbraco's view engine is created and cached before I can add new expanders to dynamically add in my theme views before the default location.

    Is there any way to solve this without resorting to string manipulation of view paths?

  • Carlos Mosqueda 239 posts 429 karma points
    Mar 15, 2022 @ 00:09
    Carlos Mosqueda

    Are you layouts the same?

    • Homepage

    • Page Layout, etc.?

    Can this be accomplished with the simple switching of a stylesheet?

    You could have the home node of the site (which ever it is on) OR have a "site configuration" node where you have your theme properties or a "theme picker" which is a dropdown list in the admin node on the node.

    Then in your "master" or primary layout template, you would just look at that doctype and get the property that would be in the site property theme selector. Then in your primary layout template you would just look through the list and then if the selection is of the one type, then show the CSS sheet, etc.
    Maybe have a base CSS file and then the picker can be an theme file that will either extend or override your base CSS file styles. That is a simple way rather than going through the file system.


    • Create a new doctype (Site configuration) and add a dropdown property (theme names) and add your theme names

    • Allow the doctype below the homepage doctype.

    • Go into your primary layout template that all your other templates would inherit from and in the 'head' tag, call your doctype

    /// Example Code Below,not tested but pretty sure it will work assuming you have your templates inheriting from the "primaryLayout" template

     Layout = null;
    var siteRoot = Model.Root();  //gets the current site root not the main Umbraco root
     var siteConfigNode = siteRoot.Descendants.Where(x => x.DocumentTypeAlias == "siteConfiguration"));
          var themePicker = siteConfigNode.Value("themePicker");
        if(themePicker == "Red"){
           <link rel="stylesheet" href="/css/themes/red.css" >
       }else if(themePicker == "Blue"){
         <link rel="stylesheet" href="/css/themes/blue.css" >        
            <link rel="stylesheet" href="/css/themes/default.css" >      
        }@*end code block*@

    Example template structure in your Umbraco Templates folder

    enter image description here

    Does that make sense?

    More or less this could be a very simple solution. If that is basically how you want the layouts to work.

    I have a similar set up on a multi site set up.

    SiteHomepage doctype allowing SiteProperties enter image description here

    enter image description here

    enter image description here

  • Stephen 21 posts 143 karma points
    Mar 15, 2022 @ 00:30

    Thanks for responding Carlos.

    Unfortunately, switching the stylesheet is not sufficient. While the document, media and data types are shared, the front end theme designs can be different enough that they require different views.

  • Carlos Mosqueda 239 posts 429 karma points
    Mar 15, 2022 @ 00:40
    Carlos Mosqueda

    If that were the case, would the theme picker still be an option and use either partials or @Html.Partial("myHomeLayoutOne"), @Html.Partial("myHomeLayoutTwo"), be in your if/else or a case statement in some standard or base templates (Home (home partials), General Page(general page partials), Search Page (search page partials)) and then you can have your default partials as well.

    Have some standard templates and have your partials determine the layout.

    Have done that too. I know others have done controllers if you want to go that route. For me doing them in the views has always been faster for my development and it seems to perform pretty well in general

  • Stephen 21 posts 143 karma points
    Mar 15, 2022 @ 02:58

    Thanks Carlos.

    My solution looks something like this:

    Example solution structure

    Content editors can already pick the theme they wish to apply to a given site via a dropdown in the Umbraco back office.

    When the Home page is requested, a custom render controller looks up the selected theme and returns the appropriate template view. So if a user views the Home page on Site A, the ~/Themes/ThemeA/Views/Home.cshtml view is rendered.

    No problems there.

    Because the designs can be so different, it is likely that the header for each theme will need to be unique, so ~/Themes/Theme A/Views/Home.cshtml would load the theme-specific partial view ~/Themes/ThemeA/Views/Partials/_Header.cshtml.

    That's how I currently have it working. Each theme has all of the views.

    Which isn't ideal. A breadcrumb for example might be generic enough that the necessary view is identical in both ThemeA and ThemeB. In cases like this, I'd like to simply not have a _Breadcrumb.cshtml file in ~/Themes/ThemeA/Views/Partials/ OR ~/Themes/ThemeB/Views/Partials/ and have the system automatically fall back to using the file in ~/Views/Partials instead. The same might also apply for some document type page templates i.e. XmlSitemap.cshtml.

    Doing things this way would make adding additional themes in future much easier, and result in a lot less code to maintain.

    This could be achieved by using a helper method to return the correct view path by doing file system look ups, but this would be slow and inelegant.


    @await Html.PartialAsync(ThemesHelper.GetThemedPartialViewPath(Model, "/Themes/Fliegl/Views/Partials/_Header.cshtml"))

    What's frustrating is that .NET has a built in way of doing what I want with view location expanders (defining a hierarchy of views to search and fall back through), but Umbraco's own view engine is getting in the way.

  • Carlos Mosqueda 239 posts 429 karma points
    Mar 15, 2022 @ 14:57
    Carlos Mosqueda

    Ahhh, I now see what you are saying.

    Doing a quick search of "Custom Umbraco View Engine", as you probably have already, I ran across an article for a V8 solution that night get you on your way. The difference between 8 and 9 probably won't be hugely significant significant.

    I will also ask around to a few of my other fellow Umbraco developers to see they have any ideas.

    Most likely you will have to be tapping into the Routes engine as well if the solution above doesn't get you on your way.

Please Sign in or register to post replies

Write your reply to: