Controls how OrchardCore renders content — placement.json rules, display drivers, shapes, zones, alternates, and editor groupings (tabs/cards/columns). Use when the user needs to position or hide a part/field, move a shape to another zone, add alternates or wrappers, build admin editor layouts, or write a display driver.
71
86%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
This skill guides you through OrchardCore's display system following project conventions.
OrchardCore renders content by building shapes (dynamic view models) from drivers, then placing each shape into a zone of the layout. Drivers decide what shapes exist and their default location; placement.json overrides where they go, whether they show, and which template renders them.
ContentItem ──drivers──▶ shapes ──placement.json──▶ zones ──templates──▶ HTMLShapeType and Metadata. The shape type (e.g. TextField, Parts_Contents_Publish, ContentPart) selects the template and the placement rule.DisplayDriver / ContentPartDisplayDriver / ContentFieldDisplayDriver. Returns IDisplayResults from Display, Edit, UpdateAsync, each with a default .Location(...).placement.json at the root of a module or theme. Filters shapes and rewrites their location/visibility/template.Content, Header, Footer, Actions, …) or an editor group (tab/card/column).| Want to… | Use |
|---|---|
| Reposition / hide a shape someone else built | placement.json |
| Move a shape to a layout zone | placement.json (place starts with /) |
| Swap the template or add alternates | placement.json (shape / alternates) |
| Lay out the admin editor (tabs/cards/columns) | placement.json groupings |
| Produce a new shape from your part/field | display driver |
| Read/write the editor model | display driver (Edit / UpdateAsync) |
Prefer placement.json for layout — no rebuild needed, theme can override.
The placement property name is the shape type, not the part name. A part without its own driver renders as ContentPart (differentiator = part name). A field renders as its field type (TextField) with differentiator {PartName}-{FieldName}.
See references/placement.md for the full differentiator table.
placement.json{
"TextField": [
{
"differentiator": "Article-Subtitle",
"displayType": "Detail",
"place": "Content:2"
}
]
}Run the site, view the content item. Use ?display debug shapes or the template's shape tracing to confirm the shape type. Wrong type → no rule matches.
| Base class | For |
|---|---|
ContentPartDisplayDriver<TPart> | a content part |
ContentFieldDisplayDriver<TField> | a content field |
SectionDisplayDriver<TModel, TSection> | settings sections |
DisplayDriver<TModel> | anything else |
public sealed class TextFieldDisplayDriver : ContentFieldDisplayDriver<TextField>
{
public override IDisplayResult Display(TextField field, BuildFieldDisplayContext context)
{
return Initialize<DisplayTextFieldViewModel>(GetDisplayShapeType(context), model =>
{
model.Field = field;
model.Part = context.ContentPart;
model.PartFieldDefinition = context.PartFieldDefinition;
})
.Location(OrchardCoreConstants.DisplayType.Detail, "Content")
.Location(OrchardCoreConstants.DisplayType.Summary, "Content");
}
public override IDisplayResult Edit(TextField field, BuildFieldEditorContext context)
{
return Initialize<EditTextFieldViewModel>(GetEditorShapeType(context), model =>
{
model.Field = field;
model.Part = context.ContentPart;
model.PartFieldDefinition = context.PartFieldDefinition;
});
}
public override async Task<IDisplayResult> UpdateAsync(TextField field, UpdateFieldEditorContext context)
{
await context.Updater.TryUpdateModelAsync(field, Prefix, f => f.Text);
return Edit(field, context);
}
}In Startup.cs:
services.AddContentPartDisplayDriver<MyPart, MyPartDisplayDriver>();
// or for a field:
services.AddContentFieldDisplayDriver<TextField, TextFieldDisplayDriver>();See references/drivers.md for shape-type helpers, the fluent .Location API, and registration variants.
| Key | Purpose |
|---|---|
place | Zone:Position. - hides, /Zone targets a layout zone |
displayType | Match Detail, Summary, SummaryAdmin, DetailAdmin, Edit |
differentiator | Distinguish reused shapes (field/part name) |
contentType | Match content type / stereotype (Art* wildcard, or array) |
contentPart | Match content items containing a part |
path | Match request path |
alternates | Add alternate shape types |
wrappers | Wrap the shape in other shapes |
shape | Substitute a different shape type |
before < 0/empty < 1 < 1.1 < 2 < 10 < after. Numeric, not alphabetic. Dot notation inserts between (5.1 is between 5 and 6).
| Modifier | Group | Example |
|---|---|---|
# | Tab | Parts:0#Media;1 |
% | Card | Parts:0%Details;2 |
| | Column | Parts:0|Left_9;1 (_9 = width, ;1 = order) |
;N after a group name = group order. _N after a column name = Bootstrap grid width (12-col).
| Zone | Purpose |
|---|---|
Content | Main content shapes |
Header / Footer | Layout regions |
Actions | Admin list row buttons |
Parts | Editor parts container |
Meta | Metadata block |
ContentPart_Edit with differentiator {ContentType}-{PartName}, not the inner XxxPart_Edit.ContentPart. Summary display of a dynamic part → ContentPart_Summary._Display, differentiator is {Part}-{Field}-{FieldType}_Display__{Mode}.references/placement.md — placement.json filters, differentiators, layout zones, editor groupingsreferences/drivers.md — driver base classes, shape helpers, fluent Location API, registrationsrc/docs/reference/modules/Placement/README.md (repo) — official placement referencesrc/docs/reference/modules/DisplayManagement/README.md (repo) — display management referencesrc/docs/reference/modules/Templates/README.md (repo) — shape differentiators and alternatesAGENTS.md (repo root) — build commandsb0e1e44
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.