Skip to content

Report 26: Plugins & Editors Comparison (v8 vs v17)

Generated: 2026-02-27 | Scope: All v8 App_Plugins property editors vs v17 TypeScript/Lit editors


1. Overview: AngularJS to TypeScript/Lit Migration

v8 Architecture (Umbraco 8 - AngularJS)

Each v8 property editor was a standalone plugin directory under App_Plugins/ containing:

App_Plugins/psHeading/
  package.manifest          <-- Plugin registration (JSON)
  propertyeditor.controller.js  <-- AngularJS controller
  propertyeditor.html       <-- AngularJS template (HTML with ng-* directives)
  propertyeditor.styles.css <-- Component styles
  img/                      <-- Icon images

The package.manifest file declared the editor alias, JS/CSS files, and view path. The AngularJS controller registered on the "umbraco" module:

angular.module("umbraco").controller("psHeading", headingWeightEditorController);

v17 Architecture (Umbraco 17 - TypeScript/Lit Web Components)

All custom editors are in a single project (Progress.CustomPropertyEditors) and bundled by Vite into a single index.js entry point:

Progress.CustomPropertyEditors/
  src/
    index.ts               <-- Single entry point, imports all manifests
    psHeading/
      manifest.ts          <-- Editor registration (TypeScript)
      heading-editor.ts    <-- Lit Web Component
    psCalculators/
      manifest.ts
      calculators-editor.ts
      calculators-modal.element.ts
    ...27 more editors...
  vite.config.ts           <-- Build config
  package.json             <-- npm dependencies

  --> Builds to --> www/App_Plugins/Progress/index.js (single bundle)

Each editor is a Lit Web Component class extending UmbElementMixin(LitElement):

@customElement('heading-editor')
export default class HeadingEditorElement
    extends UmbElementMixin(LitElement)
    implements UmbPropertyEditorUiElement { ... }

Key differences

Aspect v8 (AngularJS) v17 (TypeScript/Lit)
Framework AngularJS 1.x Lit 3.x Web Components
Language JavaScript TypeScript
Registration package.manifest (JSON) manifest.ts (TypeScript)
Module system Angular DI + angular.module() ES modules + UmbElementMixin
Build tool None (raw files served) Vite (tree-shaking, code splitting)
Output Per-plugin directory Single bundled index.js
Styling External CSS file css tagged template literal (scoped)
Template External HTML file html tagged template literal (inline)
Value binding $scope.model.value @property() value + UmbChangeEvent
Dialogs/Modals Umbraco dialogService + editorService UmbModalManagerContext + custom modal elements
Schema Implicit (value type in manifest) Explicit propertyEditorSchemaAlias

2. Side-by-Side: psHeading Editor

v8: AngularJS Property Editor

Directory: psCreditUnion/Progress.Web/App_Plugins/psHeading/

package.manifest:

{
  "javascript": ["~/App_Plugins/psHeading/propertyeditor.controller.js"],
  "css": ["~/App_Plugins/psHeading/propertyeditor.styles.css"],
  "propertyEditors": [{
    "alias": "ps.headingWeight",
    "name": "Heading Weight",
    "editor": {
      "view": "~/App_Plugins/psHeading/propertyeditor.html",
      "hideLabel": false,
      "valueType": "STRING"
    }
  }]
}

propertyeditor.controller.js:

function headingWeightEditorController($scope) {
    $scope.model.options = [
        { alias: "h1", name: "h1" },
        { alias: "h2", name: "h2" },
        { alias: "h3", name: "h3" },
        { alias: "h4", name: "h4" },
        { alias: "h5", name: "h5" },
        { alias: "h6", name: "h6" }
    ];
    $scope.model.value = $scope.model.value || $scope.model.options[0].alias;
}
angular.module("umbraco").controller("psHeading", headingWeightEditorController);

v17: TypeScript/Lit Web Component

Directory: Progress.CustomPropertyEditors/src/psHeading/

manifest.ts:

import { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor';

const propertyEditorUi: ManifestPropertyEditorUi = {
    type: "propertyEditorUi",
    alias: "ps.headingWeight",
    name: "Heading Weight",
    element: () => import('./heading-editor'),
    elementName: "heading-editor",
    meta: {
        label: "Heading Weight",
        propertyEditorSchemaAlias: "Umbraco.Plain.String",
        icon: "icon-heading",
        group: "Progress"
    }
};

export const manifests = [propertyEditorUi];

heading-editor.ts:

import { LitElement, html, css, customElement, property, state }
    from '@umbraco-cms/backoffice/external/lit';
import type { UmbPropertyEditorUiElement }
    from '@umbraco-cms/backoffice/extension-registry';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';

interface HeadingOption { alias: string; name: string; fontSize: string; }

@customElement('heading-editor')
export default class HeadingEditorElement
    extends UmbElementMixin(LitElement)
    implements UmbPropertyEditorUiElement {

    @property({ type: String }) value = '';

    @state() private _options: HeadingOption[] = [
        { alias: 'h1', name: 'H1', fontSize: '24px' },
        { alias: 'h2', name: 'H2', fontSize: '20px' },
        { alias: 'h3', name: 'H3', fontSize: '18px' },
        { alias: 'h4', name: 'H4', fontSize: '16px' },
        { alias: 'h5', name: 'H5', fontSize: '14px' },
        { alias: 'h6', name: 'H6', fontSize: '12px' }
    ];

    static styles = css`
        .heading-container { display: flex; gap: 10px; flex-wrap: wrap; }
        .heading-item { border: 1px solid #e9e9eb; width: 60px; height: 60px;
                        cursor: pointer; border-radius: 10px; font-weight: bold; }
        .heading-item--selected { border: 2px solid #2e3192; transform: scale(1.1); }
    `;

    private _selectOption(alias: string): void {
        this.value = alias;
        this.dispatchEvent(new UmbChangeEvent());
    }

    render() {
        return html`<div class="heading-container">
            ${this._options.map(o => html`
                <span class="heading-item ${this.value === o.alias ? 'heading-item--selected' : ''}"
                      style="font-size: ${o.fontSize}"
                      @click="${() => this._selectOption(o.alias)}">
                    ${o.name}
                </span>`)}
        </div>`;
    }
}

Comparison

Aspect v8 v17
Files 4 (manifest + JS + HTML + CSS) 2 (manifest.ts + editor.ts)
Lines of code ~40 (controller + HTML + CSS) ~95 (single file, includes styles + template)
Type safety None Full TypeScript types + interfaces
Styles External CSS (global scope) Shadow DOM scoped CSS
Value change $scope.model.value = ... this.value = ...; dispatchEvent(new UmbChangeEvent())
Lazy loading No (all JS loaded upfront) Yes (element: () => import())
Schema binding "valueType": "STRING" propertyEditorSchemaAlias: "Umbraco.Plain.String"

3. Complete Mapping Table: v8 App_Plugins --> v17 Editors

3.1 Custom Property Editors (migrated 1:1)

v8 App_Plugins Directory v17 Source Directory v17 Built Files Notes
psHeading/ psHeading/ heading-editor-*.js Heading weight picker (H1-H6)
psCalculators/ psCalculators/ calculators-editor-*.js + calculators-modal.element-*.js Calculator config picker with modal
psLoanBoxes/ psLoanBoxes/ loan-boxes-editor-*.js + loan-boxes-modal.element-*.js Loan box config with modal
psNews/ psNews/ news-editor-*.js + news-modal.element-*.js News layout picker with modal
psPosition/ psPosition/ position-editor-*.js Position/alignment picker
psTestimonial/ psTestimonial/ testimonial-editor-*.js Testimonial layout picker
psFaqAccordion/ psFaqAccordion/ faq-accordion-editor-*.js FAQ/accordion config
psServices/ psServices/ services-editor-*.js Services layout picker
psServicesImage/ psServicesImage/ services-image-editor-*.js Services image layout picker
psSubMenu/ psSubMenu/ sub-menu-editor-*.js Sub-menu config
psGlobalAddress/ psGlobalAddress/ global-address-editor-*.js Address display config
psGlobalGoogleMap/ psGlobalGoogleMap/ global-google-map-editor-*.js Google Maps embed config
psGlobalLeafLetMap/ psGlobalLeafLetMap/ global-leaflet-map-editor-*.js Leaflet map config
psSocialFooter/ psSocialFooter/ social-footer-editor-*.js Social media footer links
psStickySocial/ psStickySocial/ sticky-social-editor-*.js Sticky social sidebar config
psTextStringDefaultValue/ psTextStringDefaultValue/ text-string-default-value-*.js Textfield with default value
psTextAreaDefaultValue/ psTextAreaDefaultValue/ text-area-default-value-*.js Textarea with default value
psDataProtectionDPO/ psDataProtectionDPO/ data-protection-dpo-editor-*.js Data protection officer editor
psDataProtectionName/ psDataProtectionName/ data-protection-name-editor-*.js Data protection name editor
psDataProtectionSpecificStatements/ psDataProtectionSpecificStatements/ data-protection-specific-statements-editor-*.js GDPR specific statements
psDataProtectionSummaryTable/ psDataProtectionSummaryTable/ data-protection-summary-table-editor-*.js GDPR summary table
psDataProtectionWeAre/ psDataProtectionWeAre/ data-protection-we-are-editor-*.js Data protection "We Are" section
psReleaseNotes/ psReleaseNotes/ release-notes-editor-*.js Release notes display
psTweaks/ psTweaks/ tweaks-editor-*.js CSS/JS tweaks editor
FaIconpicker/ psFaIconPicker/ fa-icon-picker-editor-*.js + fa-icon-picker-modal.element-*.js Font Awesome icon picker with modal
GridSettingsMarginEditor/ GridSettingsMarginEditor/ margin-editor-*.js Grid margin settings
GridSettingsPaddingEditor/ GridSettingsPaddingEditor/ padding-editor-*.js Grid padding settings

3.2 New Editors in v17 (no v8 equivalent)

v17 Source Directory v17 Built Files Notes
psOpeningSoon/ via OpeningSoonValueConverter.cs Opening hours editor (v8 used OpeningSoon/ plugin but now has server-side converter too)
psWelcomeDashboard/ dashboard manifest Custom welcome dashboard (replaces CustomWelcomeDashboard/ AngularJS)
psEmbeddedMedia/ embedded media editor Embedded media picker (new in v17)

3.3 v8 Plugins Removed / Not Migrated

v8 App_Plugins Directory Reason for Removal
2FactorAuthentication/ Replaced by TwoFactorProviders/ (native v17 approach in www/App_Plugins/)
CDFHealthCheck/ CDF-specific; not needed in v17
ColorPickerU8/ Replaced by Umbraco.ColorPicker.EyeDropper (built-in v17)
CustomWelcomeDashboard/ Replaced by psWelcomeDashboard/ TypeScript editor
Dawoe.OEmbedPickerPropertyEditor/ Replaced by built-in Umbraco.MediaPicker3
DiploGodMode/ Third-party dev tool; not needed in production
DocTypeGridEditor/ Grid system replaced by BlockGrid (native v17)
ListViewNews/ v17 list views are configured in backoffice, no plugin needed
ListViewTestimonals/ Same as above
MeganavV8/ Third-party mega-nav; navigation rebuilt in v17 templates
Mw.UmbForms.Rte/ Forms RTE extension; v17 Forms handles this natively
NestingContently/ Nested Content helper; replaced by BlockList (native v17)
OpeningSoon/ Functionality preserved; server-side OpeningSoonValueConverter.cs + psOpeningSoon/ editor
Our.Umbraco.Matryoshka/ Nested content helper; not needed with BlockGrid
UmbracoForms/ Umbraco Forms now ships as a NuGet package, not App_Plugin
blocks/ Block editors now part of core Umbraco
psReset2FA/ Replaced by reset-2fa-editor-*.js in the Progress bundle
uSync8/ uSync now ships as NuGet package
uSyncExpansions/ uSync expansion; NuGet-based in v17
uSyncExporter/ uSync exporter; NuGet-based in v17
uSyncHistory/ uSync history; NuGet-based in v17
uSyncPacker/ uSync packer; NuGet-based in v17
uSyncPeopleEdition/ uSync people; NuGet-based in v17
uSyncPublisher/ uSync publisher; NuGet-based in v17
uSyncSnapshots/ uSync snapshots; NuGet-based in v17

3.4 Non-editor Plugins in v17 (new approach)

v17 App_Plugins Directory Purpose Notes
Progress/ Main bundle output Contains index.js + all editor bundles + images
RemoveDashboards/ Remove built-in dashboards Client-side JS approach (replaces v8 RemoveDashBoard.cs composer)
TwoFactorProviders/ 2FA login provider Replaces v8 2FactorAuthentication/
Vokseverk.ColorSelector/ Color selector Third-party; replaces ColorPickerU8/

4. Render Views Migration

The v8 Grid system used DocTypeGridEditor render views -- Razor .cshtml files inside App_Plugins/ that rendered grid content inline. In v17, the BlockGrid system uses BlockGrid component views in Views/Partials/blockgrid/Components/.

v8 Render Views in App_Plugins

v8 Render View v8 Path v17 BlockGrid Component v17 Path
DocTypeGridEditor.cshtml DocTypeGridEditor/Render/ N/A -- entire Grid framework replaced Views/Partials/blockgrid/
DocTypeGridEditorPreviewer.cshtml DocTypeGridEditor/Render/ Block preview handled by Umbraco native --
psDataProtectionDPO.cshtml psDataProtectionDPO/ BlockGrid component view Views/Partials/blockgrid/Components/
DataProtectionName.cshtml psDataProtectionName/ BlockGrid component view Views/Partials/blockgrid/Components/
psDataProtectionSpecificStatements.cshtml psDataProtectionSpecificStatements/ BlockGrid component view Views/Partials/blockgrid/Components/
DataProtectionSummaryTable.cshtml psDataProtectionSummaryTable/ BlockGrid component view Views/Partials/blockgrid/Components/
DataProtectionWeAre.cshtml psDataProtectionWeAre/ BlockGrid component view Views/Partials/blockgrid/Components/
faq.cshtml psFaqAccordion/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
LargeGoogleMap.cshtml psGlobalGoogleMap/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
LargeLeafLetMap.cshtml psGlobalLeafLetMap/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
services.cshtml psServices/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
services.cshtml psServicesImage/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
socialFooter.cshtml psSocialFooter/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
stickysocial.cshtml psStickySocial/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
subMenu.cshtml psSubMenu/Editors/RenderViews/ BlockGrid component view Views/Partials/blockgrid/Components/
HtmlTable.cshtml UmbracoForms/RazorTemplates/ Umbraco Forms package handles this --

Architectural change

In v8, render views lived alongside the editor plugin in App_Plugins/ and were invoked by the DocTypeGridEditor framework. In v17, the separation is clean:

  • Editor UI (backoffice): Progress.CustomPropertyEditors/src/ --> TypeScript/Lit --> App_Plugins/Progress/index.js
  • Render views (frontend): Views/Partials/blockgrid/Components/ --> Razor .cshtml files

This means the App_Plugins/ directory in v17 contains only editor code (JS bundles, package manifests, images). All rendering is done by standard Razor views in the Views/ directory.


5. What Was Removed / Consolidated

5.1 Third-party plugins replaced by NuGet packages

The following v8 App_Plugins/ directories were third-party packages that now install via NuGet and do not need manual App_Plugins/ directories:

  • uSync8/, uSyncExpansions/, uSyncExporter/, uSyncHistory/, uSyncPacker/, uSyncPeopleEdition/, uSyncPublisher/, uSyncSnapshots/ (8 directories --> 1 NuGet: uSync)
  • UmbracoForms/ (1 directory --> NuGet: Umbraco.Forms)

5.2 Plugins replaced by built-in Umbraco v17 features

  • DocTypeGridEditor/ --> BlockGrid (native)
  • NestingContently/ --> BlockList (native)
  • Our.Umbraco.Matryoshka/ --> BlockList nesting (native)
  • blocks/ --> Block editors (native)
  • Dawoe.OEmbedPickerPropertyEditor/ --> MediaPicker3 (native)
  • ColorPickerU8/ --> Umbraco.ColorPicker.EyeDropper (native)
  • ListViewNews/, ListViewTestimonals/ --> Backoffice list view config (native)

5.3 Development/admin tools removed

  • DiploGodMode/ -- development-only tool, not needed in production
  • CDFHealthCheck/ -- CDF-specific health check

5.4 Consolidation metrics

Metric v8 v17
Total App_Plugins directories 51 4
Custom editor directories 27 31 (in src/)
Third-party plugin directories 16 0 (all NuGet)
Infrastructure directories 8 (uSync) 0 (NuGet)
Build output files ~120 individual files 1 index.js + ~60 code-split chunks
Editor registration 27 package.manifest JSON files 1 index.ts entry point

6. Summary

The v8-to-v17 property editor migration represents a complete rewrite from AngularJS to TypeScript/Lit:

  1. All 27 custom editors were migrated 1:1 with identical aliases (e.g., ps.headingWeight) to maintain backward compatibility with existing content
  2. 16 render views moved from App_Plugins/ to Views/Partials/blockgrid/Components/
  3. 24 third-party/infrastructure plugins were eliminated (replaced by NuGet packages or built-in features)
  4. 4 new editors were added in v17 (psOpeningSoon, psWelcomeDashboard, psEmbeddedMedia, psReset2FA)
  5. The build pipeline changed from no-build (raw JS served directly) to Vite bundling with tree-shaking, code splitting, and source maps
  6. The total App_Plugins/ footprint went from 51 directories to 4 directories
Migration documentation by Double for Progress Credit Union