Custom Block Grid Views: Too Complicated

The Umbraco Grid in Umbraco has always been a powerful way to structure content, and with recent versions, it’s become even more flexible over time with the changes using Block Grid. However, with this increased flexibility, the process of creating custom backoffice views—especially with the introduction of the new Backoffice and Vite/TypeScript setup—has become far too complicated for what should be simple tasks.

In previous versions, it was relatively quick to get a basic block view up and running. Now, setting up even a simple visual preview for something like a Rich Text block or an image block requires scaffolding out an entire front-end pipeline. You’re expected to configure Vite, TypeScript, multiple HTML files, and associated build steps—just to see your content rendered in the editor.

Take the example of rendering a Rich Text field in a block. There’s no longer a default preview, and creating one manually involves an unnecessarily complex process. Realistically, we should be able to drop in a file like:

  • view.html for the block preview
  • view.css to style it

…and be done. The HTML file should support basic Umbraco tag parsing (e.g., {{ value.richTextField }}), wrap it in a styled container, and move on. We shouldn’t have to spin up a full frontend development stack for this.

This isn’t about skipping best practices or avoiding complexity where it’s warranted. For larger, more interactive blocks or custom elements—sure, the Vite pipeline makes sense. But for quick wins and everyday editor previews, the current workflow feels like overkill. When a blog post block takes two HTML files and a build pipeline to show a heading and an image in the editor, something’s gone sideways. We should not be investing that much time on admin rendering when our focus should be on the website project itself and for me goes against some of the ethos I thought Umbraco had with its approaches.

My suggestion? There should be a lightweight mode or basic HTML template option for custom block views—one that doesn’t require Vite or TypeScript, just straightforward HTML and CSS that gets the job done.

Umbraco’s strength has always been its balance between developer freedom and ease of use. Right now, it’s leaning a little too far toward “developer overhead” for common use cases. I’d love to see a middle ground introduced.

2 Likes

Hi Liam,

Totally agree, can I suggest posting this as a discussion on the GitHub repo? umbraco/Umbraco-CMS · Discussions · GitHub

As it’s more likely to be seen by the team at HQ

Also have you seen BlockPreview | Umbraco Marketplace. It is a work around which as you say isn’t ideal but worth knowing about

Matt

I can certainly share the sentiment that there is a time and a place for the “advanced” setup with Vite and TypeScript. If you feel the need to go there, which is nice if you have more than a couple of custom views, then I can recommend using the dotnet template “umbraco-extension”, which gives you a nice RCL project already set up with Vite and TypeScript & optionally includes a dashboard example with API controllers:

dotnet new umbraco-extension --include-example

However, suppose you are in the business of a few custom views. In that case, you can get quite far following the tutorial Creating your First Extension, which has a section on vanilla JavaScript elements:


Building on top of that, I have constructed an example for a block custom view here that can show a text and an image field. You need to drop two files in wwwroot/MyPackage:

First file: umbraco-package.json

{
  "name": "Simple JS Extensions",
  "version": "0.0.0",
  "extensions": [
    {
      "type": "blockEditorCustomView",
      "alias": "my.block",
      "name": "My Block",
      "element": "/App_Plugins/MyPackage/custom-block-view.js",
      "forContentTypeAlias": "imageBlock"
    }
  ]
}

Second file: custom-block-view.js

import { html, css, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';

export class BlockCustomView extends UmbLitElement {

    static properties = {
        content: { type: Object },
        settings: { type: Object },
    }

    render() {
        return html`
            <p><strong>Text:</strong> ${this.content?.text ?? 'No text'}</p>
            ${when(this.content?.image?.[0]?.mediaKey,
                    (unique) => html`<umb-imaging-thumbnail class="thumbnail" width="300" height="300" mode="crop" unique=${unique}></umb-imaging-thumbnail>`,
                    () => html`<p>No image</p>`
            )}
        `;
    }

    static styles = [
        css`
            :host {
                display: block;
            }
            
            .thumbnail {
                max-width: 300px;
                max-height: 300px;
            }
        `,
    ];
}

export default BlockCustomView;

customElements.define('my-custom-block-view', BlockCustomView);

Here is a video showcasing it in action:
2025-04-17 at 10.24.56 - Scarlet Snail

2 Likes

To pick up on something you said… what would “straightforward HTML and CSS” look like in this scenario? e.g. how would you render values or apply conditional logic / loops?

Of course, the AngularJS-style template syntax would tick those boxes - but what is a modern/supported (and lightweight) equivalent? Maybe Mustache or Handlebars? :thinking:

Anyway, it’d be good to hear thoughts from the community on this.

1 Like

What about a html template and data attributes?

Not sure how this could work with something that requires a loop though

The backoffice works with webcomponents. So as long as you have something that is technically a webcomponent, it will work. You do NOT need Lit, Vite or Typescript AT ALL. You can use vanilla javascript an not have to compile anything.

1 Like

Hi @thenexus00,

Marc from uSkinned here. I thought that you might be interested in a new feature we have recently released which improves the Visual Editing experience in Umbraco. There are a few issues with Block List and Block Grid preview which we wanted to resolve including the issue you have mentioned where it is too complicated to create the previews in the backoffice.

  • In Umbraco 13 and 15 as you have mentioned, you need two different approaches to create your frontend output and backoffice preview.
  • When editing blocks your preview is partially obscured by the slide out panel.
  • Previews are not responsive so you do not get an idea of how your block will appear on different devices.
  • Block previews only show a preview of the block section you are editing. You cannot see how your block looks within the whole website.

We have addressed these issues with our own Visual Editor.

  • We use the same partial view to render the frontend output and backoffice preview so no need for complicated or different code to render your preview in the backoffice.
  • We have created a custom preview that sits next to the slide out panel meaning you see the entire block when editing your content.
  • We have included options to display your preview on different device sizes.
  • Our preview shows the entire webpage you are editing. When you start working on a block the preview automatically scrolls to the position of the block you are editing.

visual-editor-teaser-no-sound-or-ident-920

We are currently supporting LTS versions of Umbraco and will be releasing this feature with the next LTS version of Umbraco which is 17.

You can also checkout a video of the editing process and Visual Editor in action here:

Cheers,

Marc

1 Like

This is a sales pitch more than anything. I respect Uskinned. But when your doing a certain setup and most other Umbraco developers who do not use Uskinned do what you do is not viable enough for the experiences we want to deliver to customers.

I tried this though and with the Umbraco setup this does not appear to be the case. The back office component elements and API are all through this.

I think there are two things.

  1. A short term helper solution.
  2. A better more efficient and leaner solution that matches the Umbraco ethos better and more akin to how Umbraco used to be.

Short Term:
I think either similar to 10-13 having an option for some default Blocks for a Block grid that are auto generated would be helpful.
These common elements like inserting an image, rich text editor block, heading block.

Supplying the element block and custom views.

Long Term
It needs to fit with the direction of backoffice of course and I am not fully across it all to know the best approach. I do use a lot of CMS’s with my work and I know the good and the bad.
Kentico 13 really is horrible in my view but they did TRY to copy Umbraco a bit with Page types and widgets but it is not good.
You have platforms like shopify where you have JSON data where you build your options and then the rendering is done front end but with the secion features in that editor.
Lots of other solutions.
I am not a fan of the full front end rendering in the back end though. It can lead to a lot of conflicts, popups getting in the way, Javascript errors, performance and time to Render and so on.
I like the idea of just basic output, ability to add some style to it for your back office and then the proper preview as well.
I like the Ethos and approach for Umbraco. I LOVE you can build the document types, and element types from the back office and the handling of the models and controlling elements far better than many other .NET based solutions. Getting straight into what matters. The ability for front end developers to just focus on views and rendering and so on.

Right now though having components that are far to complicated and style done in JS I do not agree with though.

I think each component should support some form MVC type structure.

  • Logic
  • View
  • Style
    I think you should be able to very quickly have access to the Element Block properties and render a view and possible style reference for that view.

If you need extra logic you can then add you component logic but it should build tags you can then use to render in your view.

You should be able to lean on the HTML Templates and web components:

You should be able to just reference say a single JSON file when you setup the element in the block grid for a custom view.
This references the web component, html template, style etc it needs to then render.

1 Like

I just wanted to add on to this.

To render a block item which just has a rich text field to render its HTML:

  • Documentation on how you derive the content based on the header example is wrong. I had to a JSON object output as there are no documentation or reference you can get. The markup for rich text:
{
  "richText": {
    "markup": "<p>Col 2</p>",
    "blocks": {
      "layout": {},
      "contentData": [],
      "settingsData": [],
      "expose": []
    }
  }
}

So based on the documentation this.content?. richText did not work.
Then you get errors because of potential unsafe html so you got to use unsafe html.

All up just to render the HTML the typescript file is only 50 odd lines of code but it was a pain to get. It compiles into a JS file thats over 14000 lines of code which is insane.

You have all the config you got to setup and include as well of course.

Then with the styles in there, even if you just wanted to tweak stuff the process you got to do is just over the top.

All this to just display a rich text output in the new Umbraco back office grid editor.

I respect the power, I do not respect the over complicated nature of this process and I can see why not as many plugins have been switched over and the time take for many to have into the new structure.

I also re-tried just pure Javascript and web componet and it in no way works.

  • The documentation is lacking
  • The documentation is not accurate
  • The process has 5? different processes to setup based on guides I have found. I do not think there should ever be multiple roots for the same output in my opinion, it creates inconsistency and confusion
  • So much for so little.

It not that I have found this difficult or anything but its to much for what you get and at this point of time not easy to find the proper information.

As I noted with my suggestion, it needs to be a lot simpler and easier to manage.
Especially for basic grid block stuff and probably for back office addons as well you need to be able to more easily manage and render views and update them and be able to get things running and rendering more efficiently.
It is not very Umbraco right now.

1 Like

In Addition loosing things like magic strings so you can no longer place in a label something like this:
{{ !accordionHeadline || accordionHeadline.trim() === ‘’ ? ‘Accordion Item’ : accordionHeadline }}

And now the solution is more like this:

label: (data: any) => {
  const heading = data.accordionHeadline?.trim();
  return heading || 'Accordion Item';
}

I think is just a backward step.

1 Like

@thenexus00 Thank you! :folded_hands:

There’s much to comment on here (which we will in due course), but I wanted to say that it’s much needed feedback and that I hear you. :umbraco-heart:

In terms of the replacement of AngularJS expressions, we do have Umbraco Flavored Markdown (UFM). As the main developer responsible for this feature, I’m fully aware that it’s not a like-for-like replacement, there is no quazi-eval() feature (for simple conditional logic), and I appreciate that’s frustrating!
We had much discussion about this, but we didn’t want to rush in and feel that we were re-introducing technical debt… specifically things like AngularJS expression filters, e.g. ncNodeName had caused much frustration over the years. I digress.
TL;DR, Am I happy with the current state of UFM? No. Do I want it to improve? Hell yes!

About vanilla JS in the backoffice… I’m curious if @jacob’s example (above) could work for you? :thinking:

Thanks, Lee.

1 Like

Exactly! I’m thinking along the lines of a templating engine, like Mustache or Handlebars, (or something else that I’m not aware of).

I am one of the founders of uSkinned and I do not hide this fact in my user name. Of course its partly a sales pitch as I am mentioning my own platform however when a post asks a question which we have solved, why would I not want to let people know about it. It’s important to remember that the Umbraco forum is a place for sharing different perspectives and ideas. I respect your point of view however our Visual Editor solves all of the issues you have mentioned so its worth letting people know about it so that they can make their own decisions.

1 Like

I do not find that the new documentation indexes well in google so thank you Lee for the UFM doc.

This is work and off field but this has given me a thought here.
Web components has html templating, it is a thing. You have created UFM but that will take time to develop out for other uses. This lead me to an idea .

The company I work for was a lead partner using Adobe Business Catalyst. A CMS out of Australia bought by Adobe. They wanted to have extra logic to templating and I was a lead developer who over time had a lot of involvement with them, many features could be direct attributed to myself.

One of those and one I presented for Adobe on at Max was Liquid Markup. Shopify made that open source and there many solutions who used that within .NET. dotliquid was one and I think one that is open source and still maintained is Fluid but many have taken liquid and implemented their own. I was told by one of the Adobe engineers at the time that it was not too difficult.

UMB would take time to build out.
I would not go crazy and replace razor views with liquid markup rendering but you could use liquid markup for back end rendering.
It could be used in field labels etc, it could be used for logic build backoffice applications and it could be used for custom views.
It would tie directly into associated models and especially in custom views would be easier to use. You could then create a .net class to define extra properties and so on for the liquid rendering.

<ul id="products">
  {% for product in products %}
    <li>
      <h2>{{product.name}}</h2>
      Only {{product.price | price }}

      {{product.description | prettyprint | paragraph }}
    </li>
  {% endfor %}
</ul>

I have seen a few ways to reference a CSS style be it via the include tag, render tag or asset reference.

It has been used by a lot of platforms and languages and I think unused by many platforms, some large system would have a lot of issues resolved if they used it. Kentico created their “K sharp” and then have a complete mess with their portal CMS backend and .NET core MVC website front end now. They could have benefited greatly with 12 and under using liquid instead of their own thing and could have even used liquid today to help with various templating for their widgets etc.

Further a field Umbraco could explore a combination the GraphQL and alternate template rendering with Liquid in Umbraco and coupled with how easy it is to build document types you could taret smaller agencies and solo creators with little to no knowledge of .NET and have the cloud based solution or a version of being able to create sites with just liquid markup templates.

Again here I am just thinking about the back end use but I really believe this is worth exploring.

1 Like

It was not a means of Disrespect Marc,
I am a big fan of core. I love and appreciate anyone that creates solutions and addons for things and have directly provided feedback to many to help them be better including uSkinned.

My approach does not match the uSkinned concepts but I think it is great many use it and it exists. For me in this discussion though what Umbraco has changed and the direction in this area and just how at its core right now it is 14 and 15 versions in and still clunky and buggy it needs work.

I think trying to help get the core experience back on track and in the right direction and trying to help with that is important. Anyone who then expands off that is great but I am not a big fan of having to look at a 3rd party because the core is broken.

I hope that makes sense.

For reference for anyone.

I spent time and while the documentation is not clear I was able to actually figure out you can just code direct JS components as mentioned here but it took a bit of work to figure out.

This is what I ended up with to render content from rich text.

import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { html, css, LitElement } from '@umbraco-cms/backoffice/external/lit';
import { unsafeHTML } from 'https://unpkg.com/[email protected]/directives/unsafe-html.js?module';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';

class RichTextBlockView extends UmbElementMixin(LitElement) {
	static get properties() {
		return {
			content: { attribute: false }
		};
	}

	render() {
		const htmlContent = this.content?.richText?.markup ?? '<em>No content</em>';
		return html`<div>${unsafeHTML(htmlContent)}</div>`;
	}

	static styles = [
		UmbTextStyles,
		css`
			:host {
				display: block;
				height: 100%;
				box-sizing: border-box;
				color: black;
				border-radius: 9px;
				border: 1px dashed #ccc;
				padding: 12px;
			}
		`
	];
}

customElements.define('richtext-block-view', RichTextBlockView);

Key notes are on the definition and no export etc as you would in your typescript.
Since you need to use unsafeHTML to render HTML I had to reference from unpkg to do so.

In the umbraco-package.json as well I had to ensure I mentioned “elementName” as well as “element” and “name” for it to work as well.

Regarding UFM:

I do not think it is working properly.
Use case:

Element called “accordionList” has a block list property.
That block list has an element called “accordionItem”
This has a property called “accordionHeadline”

  • In the block list property editor
  • Clicking accordionBlock and clicking label field
  • Entering: {umbValue: accordionHeadline} Renders empty (There is a value)
  • Entering: {umbValue: accordionHeadline} Renders empty (There is a value)
  • Entering {accordionHeadline} renders exactly as entered with braces
  • Entering {umbContentName: accordionHeadline} renders nothing

Note: Have spaces after first brace and before last brace will prevent rendering

The only output that worked was:
{=accordionHeadline}

And then as noted it has limited filters and no conditionals.

Image rendering custom view

This is what I have for basic Image rendering…

import { html, css, LitElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbImagingRepository } from '@umbraco-cms/backoffice/imaging';

class ImageBlockView extends UmbElementMixin(LitElement) {
	static get properties() {
		return {
			content: { attribute: false },
			imageUrls: { type: Object }
		};
	}

	constructor() {
		super();
		this.imageUrls = new Map();
		this._imagingRepository = new UmbImagingRepository(this);
	}

	// Watch for content updates
	updated(changedProperties) {
		super.updated?.(changedProperties);
		if (changedProperties.has('content') && this.content?.image?.length) {
			this._fetchImageUrls();
		}
	}

	async _fetchImageUrls() {
		const imageItems = this.content.image;
		for (const item of imageItems) {
			if (item.mediaKey && !this.imageUrls.has(item.mediaKey)) {
				const url = await this._resolveImageUrl(item.mediaKey);
				if (url) {
					this.imageUrls.set(item.mediaKey, url);
					this.requestUpdate(); // Trigger re-render
				}
			}
		}
	}

	async _resolveImageUrl(mediaKey) {
		try {
			const { data } = await this._imagingRepository.requestThumbnailUrls([mediaKey], 1200, 0); // 0 for auto height
			return data?.[0]?.url ?? '';
		} catch (err) {
			console.warn('Failed to load media URL for:', mediaKey, err);
			return '';
		}
	}

	render() {
		const classes = this.content?.imageCustomClasses?.join(' ') ?? '';

		if (!this.content?.image?.length) {
			return html`<em>No image selected</em>`;
		}

		return html`
			<div class="image-block">
				${this.content.image.map(item => {
					const src = this.imageUrls.get(item.mediaKey) || '';
					return src
						? html`<img src="${src}" class="${classes}" alt="" style="max-width: 100%; height: auto;" />`
						: html`<em>Loading image...</em>`;
				})}
			</div>
		`;
	}

	static styles = [
		css`
			:host {
				display: block;
				box-sizing: border-box;
				padding: 12px;
				border: 1px dashed #ccc;
				border-radius: 9px;
				background: #fff;
			}
		`
	];
}

customElements.define('image-block-view', ImageBlockView);

It is fine but it should help again to iterate my point that it is a bit much to render out an image.
In a theory of liquid markup use it could look like this…

<!-- App_Plugins/MyBlockViews/Image.html -->
<div class="image-block">
	<img src="{=image[0].mediaKey | umbMediaUrl}" alt="" class="{=imageCustomClasses | join}" />
</div>

With manifest…

{
  "type": "blockEditorCustomView",
  "alias": "My.ImageBlockView",
  "name": "Image Block View",
  "elementName": "umb-static-template-view",
  "element": "/App_Plugins/MyBlockViews/Image.html",
  "forContentTypeAlias": "imageBlock",
  "forBlockEditor": "block-grid"
}

How I see the sort of options:

Approach Complexity Support Ideal For
Current Lit-based Custom Views High Native in Umbraco 15 Power users, complex views
Liquid Markup (your idea) Medium (implementation needed) Not supported yet Simpler templating, server-side logic
HTML Template + Custom Filter Syntax Low/Medium Could be a core enhancement Clean, declarative use
Frontend-only View with UFM & {=} expressions Medium-Low Already partially supported but requires build out Text + media display mainly

With UFM if that is considered to be expanded and while I know it is rendering out of a Javascript based solution at the end of the day, something like Liquid markup has all the elements you would look to flesh out already built.