Skip to main content
umbraco-validation-context Implement form validation using UmbValidationContext in Umbraco backoffice
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/umbraco/Umbraco-CMS-Backoffice-Skills --skill umbraco-validation-contextEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Ocupaciones relacionadas
SOC
Basado en la clasificación ocupacional SOC
name umbraco-validation-context description Implement form validation using UmbValidationContext in Umbraco backoffice version 1.0.0 location managed allowed-tools Read, Write, Edit, WebFetch
Umbraco Validation Context
What is it?
UmbValidationContext provides a centralized validation system for forms in the Umbraco backoffice. It manages validation messages using JSON Path notation, supports both client-side and server-side validation, and enables reactive error counting for tabs and sections. This is essential for multi-step forms, workspace editors, and any UI that requires comprehensive validation feedback.
Documentation
Always fetch the latest docs before implementing:
Reference Examples
The Umbraco source includes working examples:
Validation Context Dashboard : /Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/validation-context/
This example demonstrates multi-tab form validation with error counting.
Custom Validation Workspace Context : /Umbraco-CMS/src/Umbraco.Web.UI.Client/examples/custom-validation-workspace-context/
This example shows workspace-level validation patterns.
Related Foundation Skills
Workflow
Fetch docs - Use WebFetch on the URLs above
Ask questions - What fields? What validation rules? Multi-tab form?
Generate files - Create form element with validation context
Explain - Show what was created and how validation works
Basic Setup
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit' ;
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element' ;
import {
UMB_VALIDATION_CONTEXT ,
umbBindToValidation,
UmbValidationContext ,
} from '@umbraco-cms/backoffice/validation' ;
import type { UmbValidationMessage } from '@umbraco-cms/backoffice/validation' ;
@customElement ('my-validated-form' )
export class MyValidatedFormElement extends UmbLitElement {
readonly validation = new UmbValidationContext (this );
@state ()
private _name = '' ;
@state ()
private _email = '' ;
@state ()
private _messages ?: UmbValidationMessage [];
constructor ( ) {
super ();
this .consumeContext (UMB_VALIDATION_CONTEXT , ( ) => {
. (
validationContext?. . ,
{
. = messages;
},
);
});
}
( ) {
html` ;
}
# ( ) {
isValid = . . ();
(isValid) {
. ( );
}
}
}
Multi-Tab Form with Error Counting
import { html, customElement, state, when } from '@umbraco-cms/backoffice/external/lit' ;
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element' ;
import { UmbValidationContext , umbBindToValidation } from '@umbraco-cms/backoffice/validation' ;
@customElement ('my-tabbed-form' )
export class MyTabbedFormElement extends UmbLitElement {
readonly validation = new UmbValidationContext (this );
@state () private _tab = '1' ;
@state () private _totalErrors = 0 ;
@state () private _tab1Errors = 0 ;
@state () private _tab2Errors = 0 ;
@state () private _name = '' ;
@state () private _email = '' ;
@state () private _city = '' ;
@state () private _country = '' ;
constructor ( ) {
super ();
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
. (
. . . ( ),
{
. = [... (messages. ( x. ))]. ;
}
);
}
( ) {
html` ;
}
# ( ) {
html` ;
}
# ( ) {
html` ;
}
# ( ) {
. = (e. ). ( ) ?? ;
}
# ( ) {
isValid = . . ();
(!isValid) {
. ( );
}
}
}
Server-Side Validation Errors
Add server validation errors after an API call:
async #handleSave ( ) {
const isValid = await this .validation .validate ();
if (!isValid) return ;
try {
const response = await this .#saveToServer ();
if (!response.ok ) {
const errors = await response.json ();
for (const error of errors.validationErrors ) {
this .validation .messages .addMessage (
'server' ,
error.path ,
error.message ,
crypto.randomUUID ()
);
}
}
} catch (error) {
console .error ('Save failed:' , error);
}
}
Key APIs
UmbValidationContext
const validation = new UmbValidationContext (this );
const isValid = await validation.validate ();
validation.messages ;
Validation Messages
validation.messages .addMessage (source, path, message, key);
validation.messages .removeMessagesBySource ('server' );
this .observe (
validation.messages .messagesOfPathAndDescendant ('$.form.tab1' ),
(messages ) => { }
);
this .observe (
validation.messages .messages ,
(messages ) => { }
);
umbBindToValidation Directive
${umbBindToValidation (this , '$.form.fieldName' , fieldValue)}
JSON Path Notation
Validation uses JSON Path to identify fields:
Path Description $.formRoot form object $.form.nameName field $.form.tab1.emailEmail field in tab1 $.form.items[0].valueFirst item's value $.form.items[*].nameAll item names
Validation Message Interface
interface UmbValidationMessage {
source : string ;
path : string ;
message : string ;
key : string ;
}
Best Practices
Use JSON Path hierarchy - Organize paths by tab/section for easy error counting
Wrap inputs - Use <uui-form-validation-message> around inputs
Clear server errors - Remove old server errors before new validation
Unique keys - Use crypto.randomUUID() for server error keys
Observe specific paths - Use messagesOfPathAndDescendant for scoped error counts
Show counts on tabs - Display error badges to guide users to problems
That's it! Always fetch fresh docs, keep examples minimal, generate complete working code.
validationContext
this
observe
messages
messages
(messages ) =>
this
_messages
'observeValidationMessages'
override
render
return
<uui-form >
<form >
<div >
<label > Name</label >
<uui-form-validation-message >
<uui-input
type ="text"
.value =
${this ._name}
@input =
${(e: InputEvent) => (this ._name = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.name' , this ._name)}
required
> </uui-input >
</uui-form-validation-message >
</div >
<div >
<label > Email</label >
<uui-form-validation-message >
<uui-input
type ="email"
.value =
${this ._email}
@input =
${(e: InputEvent) => (this ._email = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.email' , this ._email)}
required
> </uui-input >
</uui-form-validation-message >
</div >
<uui-button look ="primary" @click =
${this .#handleSave}
> Save</uui-button >
</form >
</uui-form >
<pre >
${JSON .stringify(this ._messages ?? [], null , 2 )}
</pre >
`
async
handleSave
const
await
this
validation
validate
if
console
log
'Form is valid!'
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form'
(messages ) =>
this
_totalErrors
new
Set
map
(x ) =>
path
length
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form.tab1'
(messages ) =>
this
_tab1Errors
new
Set
map
(x ) =>
path
length
this
observe
this
validation
messages
messagesOfPathAndDescendant
'$.form.tab2'
(messages ) =>
this
_tab2Errors
new
Set
map
(x ) =>
path
length
override
render
return
<uui-box >
<p > Total errors:
${this ._totalErrors}
</p >
<uui-tab-group @click =
${this .#onTabChange}
>
<uui-tab ?active =
${this ._tab === '1' }
data-tab ="1" >
Tab 1
${when(
this ._tab1Errors,
() => html`<uui-badge color ="danger" > ${this ._tab1Errors} </uui-badge > `
)}
</uui-tab >
<uui-tab ?active =
${this ._tab === '2' }
data-tab ="2" >
Tab 2
${when(
this ._tab2Errors,
() => html`<uui-badge color ="danger" > ${this ._tab2Errors} </uui-badge > `
)}
</uui-tab >
</uui-tab-group >
${when(this ._tab === '1' , () => this .#renderTab1())}
${when(this ._tab === '2' , () => this .#renderTab2())}
<uui-button look ="primary" @click =
${this .#handleSave}
> Save</uui-button >
</uui-box >
`
renderTab1
return
<uui-form >
<form >
<label > Name</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._name}
@input =
${(e: InputEvent) => (this ._name = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab1.name' , this ._name)}
required
> </uui-input >
</uui-form-validation-message >
<label > Email</label >
<uui-form-validation-message >
<uui-input
type ="email"
.value =
${this ._email}
@input =
${(e: InputEvent) => (this ._email = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab1.email' , this ._email)}
required
> </uui-input >
</uui-form-validation-message >
</form >
</uui-form >
`
renderTab2
return
<uui-form >
<form >
<label > City</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._city}
@input =
${(e: InputEvent) => (this ._city = (e.target as HTMLInputElement).value)}
${umbBindToValidation(this , '$.form.tab2.city' , this ._city)}
required
> </uui-input >
</uui-form-validation-message >
<label > Country</label >
<uui-form-validation-message >
<uui-input
.value =
${this ._country}
@input =
${(e: InputEvent) => (this ._country = (e.target as HTMLInputElement).value)}
required
> </uui-input >
</uui-form-validation-message >
</form >
</uui-form >
`
onTabChange
e : Event
this
_tab
target
as
HTMLElement
getAttribute
'data-tab'
'1'
async
handleSave
const
await
this
validation
validate
if
console
log
'Form has validation errors'