-
Blazor, Razor or Headless?
-
What is your preffered JS framework/library
-
What is your preferred CSS framework/library
-
Do you separate frontend in your project to and use something like Vite/Parcel for building or is it part of the .net project?
So i will mostly be sticking with Razor and headless . i skew Back end..
I would like to do more astro and static .. so frameworks an complex things are not wht i’d be doing with those.. otherwise i’ll use whatever someone else decides..
Hi Jukka-Pekka ![]()
- Razor
- Plain, vanilla JavaScript
- Less CSS (for separating files and nesting)
- Separate frontend project that’s compiled (using CodeKit), and copied to a folder in the .net project
/Chriztian
We use headless frontends in React. The reason is that we have a number of react developers that can focus on the frontend stack seperate from the CMS and also have component that are reusable on other CMSes. They use tailwind for css.
It has it’s advantages and disadvantages. The backend and frontend are still relatively tighly coupled, so I think we would probably also get the same result in the end using razor views. And in some cases it’s actually easier to use razor views because of a number of things that are possible out of the box that you need to build yourself in a headless scenario.
So in our case the frontend is a completely seprate project in a different tech stack, in s different repo and hosted as a seperate entity.
Umbraco extensions are Lit + Vite + Typescript, like Umbraco does.
We still have feet in both camps.
Regardless of whether Umbraco serves the client as well, or we opt for a headless approach, the stack itself doesn’t change much.
- We write HTML, CSS, and JavaScript.
- Modern CSS gives us most of what we used SASS for. Variables can pass through to the Shadow DOM. Nesting was a huge win and a necessary feature.
- Our sites are built as web components in regular JavaScript code.
(Moving to TS is debated repeatedly.)
- FED assets are designed in Penpot, implemented in Storybook until we feel they are feature complete and ready to be used in our sites. We essentially have a catalog of modern web components to borrow from at any time.
- Storybook is our build system.
- No FED frameworks by default.
- Only for more complicated interactivity, we lean towards Lit.
When Umbraco is used solely as the CMS and not serving the client application, we utilize the Astro framework. I forget why, but I can tell you we moved from Netlify to Cloudflare Pages. It is possible that, since we are already part of the CF ecosystem, we decided to host with them, thereby reducing the number of products/services we rely on. (Netlify is still an excellent choice.)
I’m considering incorporating the View Transition API into our designs, which has me debating whether or not to include HTMX. I created a demo years ago where I replaced the Default Controller Action. I could make requests to the Umbraco server, and whether I include the Accept: application/json header or not, the server responds with either the Content Delivery API model for that page or simply the HTML partial.
(Back then, there was no Content Delivery API, so we had mapped everything to lighter models for JSON serialization.)
Fun topic! Could sit here for days. Thanks for posting!
I always wonder why that even is a debate. It’ll make your components so much less error prone and catch errors compile time instead of runtime. Clearly there is thought put into making components and modularity and reuse things. That’s not some basic approach of a beginning developer. And it you go through all that effort why you wouldn’t use typescript is beyond me to be honest ![]()
- Razor MVC
- For javascript we generally use Alpine.JS and HTMX, and simply import those dependencies from a CDN. Our own scripts is more often than not simply inlined in to the pages/blocks where they are used. We have a bunch of “special” tag helpers, that prevents eg. multiple rendering of a script block, from a block view. Eg. if you have multiple carousel blocks on the same page, the script controlling the Alpine component is only rendered once to the page, and only if there is a carousel on the page.
- Not specifically suited for Umbraco, but we use TailwindCSS. Works really well across different projects and developers, as you get a defined language for how to use css, and constraints for what you can do easily. A bonus thing for Tailwind is that it doesn’t really prevent you from doing your own CSS if you need to do crazy ass modern css, like custom view transitions etc.
- No, its just part of the .net project. Our frontend build is quite simple, it’s basically just the TailwindCSS cli tool. Javascript dependencies is more often than not pulled from a CDN (with a serverside tag helper similar to this, that then pulls the script files to our own server, and serves them from there).
I’ve played around with Alpine.js and HTMX, but they never really clicked for me. I’m pretty comfy with JS frameworks, so I usually just do vanilla or throw a react .js on the page for the interactive bits. Yeah, it’s probably a few more kb, but in practice you can’t really feel the difference.
I really need to give Tailwind + .NET another shot. Last time I tried it, I couldn’t get IntelliSense working properly in Visual Studio, so I gave up a bit too fast.
The TagHelper idea is super nice. Locking down where CSS/JS comes from and only allowing trusted sources is exactly how it should be. I’m definitely going to look into that. tak for the link!
There is an unofficial extension for TailwindCSS in Visual Studio. It’s almost as good os the official one for VS Code.
If you’re using JavaScript modules, there’s no need to do that. You can just load the module in your block’s markup however many times you want since the browser only loads the first instance of a module (which is a singleton).
// carousel.js
console.log('Ahoy!');
<script type="module" src="/js/carousel.js"></script>
<!-- markup... -->
<script type="module" src="/js/carousel.js"></script> <!-- browser will ignore this -->
<!-- markup... -->
<script type="module" src="/js/carousel.js"></script> <!-- browser will ignore this -->
<!-- markup... -->
Console:
Ahoy!
One thing to mention here, is that modules will be delayed until the DOM is loaded. The upshot is that you can be certain any DOM element is present. But it has defered loading, which is not always the best solution.
I know, but then we have inline scripts for Alpine components like
<ec-push-stack id="footer" key="myComponent">
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('myComponent', () => ({
myValue: false,
toggle() {
this.myValue = !this.myValue;
}
}));
});
</script>
</ec-push-stack>
No, but it really should be - it will always result in the best experience/pagespeed for a “traditional” website.
Presumably htmx.config.inlineScriptNonce, or maybe CSP isn’t required? ![]()
Well, you also have async, which is not defered, so that was what I meant. Sometimes you don’t need to wait for the DOM to load but just execute as soon as the script is available. But in the context of having multiple script tags with the same script, modules are great. We had an image processor before going headless that also had a partial view that was used to rendering the image. I included the script tag for the required script in that partial view as a module because I knew that it would only load once when there are multiple partial views with that script tag on the same page.
- Blazor as razor is getting deprecated and Blazor can do hybrid now, so both server-side and client-side updates.
- I usually use vanilla JS with a css framework, as LESS and other translated css systems often do not support recent css features, I don’t tend to use them. I never use NPM systems due to the ongoing (what is it like 5 years now?) supply chain issues with NPM and the overhead of translated JS frameworks.
- I change depending on what is needed, I often start with a simple css framework and modify it quite a bit though.
- All components are in a separate project, often FE is a separate project for me too.
Razor + HTMX. Tad bit of inline javascript. All I need!
Really interesting read here in this thread.
-
Some Razor+MVC and some JS+headless.
-
We are now supplementing our existing ASP.NET MVC projects with React (or sometimes Alpine or HTMX) and generally any new projects will likely use NextJS and headless Umbraco. NextJs really is awesome. There would have to be a strong reason why we built an Umbraco-based frontend at this point.
As people have said, it allows a better separation of development and also it’s allowed us to consider other aspects of development more seriously, e.g. containerization.
-
We’re CSS agnostic. Whatever suits the job.
-
Yes, we always separate the FE from the .NET. However, for the Razor+MVC projects, we always have the FE build pipeline copy the output into the MVC project. In all projects the FE build pipeline output is excluded from git and the FE pipeline is rerun as part of the build and deployment process.
CSP not required in those projects ![]()
But there are options available if CSP is required and a nonce is needed!
Since almost nobody talks about Heartcore I’ll chime in my stack.
Blazor, Razor or Headless?
Headless, this is the only thing that is supported in Heartcore.
-
What is your preffered JS framework/library
Astro or Next.js, preferably Astro. Statically build any page and any part of pages that can be statically built (SSG or cached SSR) -
What is your preferred CSS framework/library
Tailwind, pure and simple. It feels weird when you start out, and looks ugly. But once you get used to it Tailwind really shines. Works best if you split up your code into re-usable components. For pages that don’t split up code into reusable components Tailwind makes less sense, then custom CSS classes are suddenly useful again (personally I don’t use @apply with tailwind unless I really have to) -
Do you separate frontend in your project to and use something like Vite/Parcel for building or is it part of the .net project?
Both Astro and Next.js come pre-configured with bundler and build tools. If I had used pure React I would have gone for Vite. But I don’t see any good reason for using pure React.
My “backend” is API-layers in Next or Astro that talk with Umbraco Heartcore, auth and the likes.