- 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:
- Foundation
- :
- https://docs.umbraco.com/umbraco-cms/customizing/foundation
- Extension Registry
- :
- https://docs.umbraco.com/umbraco-cms/customizing/extending-overview/extension-registry
- 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
- State Management
-
- For observing validation state changes
- Reference skill:
- umbraco-state-management
- Context API
- For consuming validation context
Reference skill:
umbraco-context-api
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
{
// Create validation context for this component
readonly
validation
=
new
UmbValidationContext
(
this
)
;
@
state
(
)
private
_name
=
''
;
@
state
(
)
private
_email
=
''
;
@
state
(
)
private
_messages
?
:
UmbValidationMessage
[
]
;
constructor
(
)
{
super
(
)
;
// Observe all validation messages
this
.
consumeContext
(
UMB_VALIDATION_CONTEXT
,
(
validationContext
)
=>
{
this
.
observe
(
validationContext
?.
messages
.
messages
,
(
messages
)
=>
{
this
.
_messages
=
messages
;
}
,
'observeValidationMessages'
)
;
}
)
;
}
override
render
(
)
{
return
html
`
${
JSON
.
stringify
(
this
.
_messages
??
[
]
,
null
,
2
)
}
` ; } async
handleSave
(
)
{
const
isValid
=
await
this
.
validation
.
validate
(
)
;
if
(
isValid
)
{
// Form is valid, proceed with save
console
.
log
(
'Form is valid!'
)
;
}
}
}
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
;
// Form fields
@
state
(
)
private
_name
=
''
;
@
state
(
)
private
_email
=
''
;
@
state
(
)
private
_city
=
''
;
@
state
(
)
private
_country
=
''
;
constructor
(
)
{
super
(
)
;
// Observe total errors
this
.
observe
(
this
.
validation
.
messages
.
messagesOfPathAndDescendant
(
'$.form'
)
,
(
messages
)
=>
{
this
.
_totalErrors
=
[
...
new
Set
(
messages
.
map
(
(
x
)
=>
x
.
path
)
)
]
.
length
;
}
)
;
// Observe Tab 1 errors (using JSON Path prefix)
this
.
observe
(
this
.
validation
.
messages
.
messagesOfPathAndDescendant
(
'$.form.tab1'
)
,
(
messages
)
=>
{
this
.
_tab1Errors
=
[
...
new
Set
(
messages
.
map
(
(
x
)
=>
x
.
path
)
)
]
.
length
;
}
)
;
// Observe Tab 2 errors
this
.
observe
(
this
.
validation
.
messages
.
messagesOfPathAndDescendant
(
'$.form.tab2'
)
,
(
messages
)
=>
{
this
.
_tab2Errors
=
[
...
new
Set
(
messages
.
map
(
(
x
)
=>
x
.
path
)
)
]
.
length
;
}
)
;
}
override
render
(
)
{
return
html
`
Total errors: ${ this . _totalErrors }
<uui-tab-group @click= ${ this .
onTabChange
}
Tab 1 ${ when ( this . _tab1Errors , ( ) => html <uui-badge color="danger"> ${ this . _tab1Errors } </uui-badge>) }Tab 2 ${ when ( this . _tab2Errors , ( ) => html ${ when ( this . _tab === '1' , ( ) => this .<uui-badge color="danger"> ${ this . _tab2Errors } </uui-badge>) }
renderTab1
( ) ) } ${ when ( this . _tab === '2' , ( ) => this .
renderTab2
( ) ) } <uui-button look="primary" @click= ${ this .
handleSave
}
Save ` ; }
renderTab1
(
)
{
return
html
`
` ; }
renderTab2
(
)
{
return
html
`
` ; }
onTabChange
( e : Event ) { this . _tab = ( e . target as HTMLElement ) . getAttribute ( 'data-tab' ) ?? '1' ; } async
handleSave
( ) { const isValid = await this . validation . validate ( ) ; if ( ! isValid ) { console . log ( 'Form has validation errors' ) ; } } } Server-Side Validation Errors Add server validation errors after an API call: async
handleSave
( ) { // First validate client-side const isValid = await this . validation . validate ( ) ; if ( ! isValid ) return ; try { // Call API const response = await this .
saveToServer
(
)
;
if
(
!
response
.
ok
)
{
// Add server validation errors
const
errors
=
await
response
.
json
(
)
;
for
(
const
error
of
errors
.
validationErrors
)
{
this
.
validation
.
messages
.
addMessage
(
'server'
,
// Source
error
.
path
,
// JSON Path (e.g., '$.form.name')
error
.
message
,
// Error message
crypto
.
randomUUID
(
)
// Unique key
)
;
}
}
}
catch
(
error
)
{
console
.
error
(
'Save failed:'
,
error
)
;
}
}
Key APIs
UmbValidationContext
// Create context
const
validation
=
new
UmbValidationContext
(
this
)
;
// Validate all bound fields
const
isValid
=
await
validation
.
validate
(
)
;
// Access messages manager
validation
.
messages
;
Validation Messages
// Add a message
validation
.
messages
.
addMessage
(
source
,
path
,
message
,
key
)
;
// Remove messages by source
validation
.
messages
.
removeMessagesBySource
(
'server'
)
;
// Observe messages for a path and descendants
this
.
observe
(
validation
.
messages
.
messagesOfPathAndDescendant
(
'$.form.tab1'
)
,
(
messages
)
=>
{
/ handle messages /
}
)
;
// Observe all messages
this
.
observe
(
validation
.
messages
.
messages
,
(
messages
)
=>
{
/ handle all messages /
}
)
;
umbBindToValidation Directive
// Bind an input to validation
$
{
umbBindToValidation
(
this
,
'$.form.fieldName'
,
fieldValue
)
}
JSON Path Notation
Validation uses JSON Path to identify fields:
Path
Description
$.form
Root form object
$.form.name
Name field
$.form.tab1.email
Email field in tab1
$.form.items[0].value
First item's value
$.form.items[*].name
All item names
Validation Message Interface
interface
UmbValidationMessage
{
source
:
string
;
// 'client' | 'server' | custom
path
:
string
;
// JSON Path
message
:
string
;
// Error message text
key
:
string
;
// Unique identifier
}
Best Practices
Use JSON Path hierarchy
- Organize paths by tab/section for easy error counting
Wrap inputs
- Use