orchardcore-razor
Skill for building Razor views in Orchard Core themes and modules. Covers tag helpers, shape rendering, resource management, IOrchardHelper extensions, layout patterns, and _ViewImports.cshtml setup.
Files2
View on GitHubSKILL.mdsrc/CrestApps.AgentSkills/orchardcore/orchardcore-razor/SKILL.md
GitHubOrchard Core Razor Views - Prompt Templates
_ViewImports.cshtml Setup
Every Orchard Core theme or module should include a _ViewImports.cshtml file at the root of its Views folder.
Minimum Required Directives
@inherits OrchardCore.DisplayManagement.Razor.RazorPage<TModel>
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, OrchardCore.DisplayManagement
@addTagHelper *, OrchardCore.ResourceManagement
@addTagHelper *, OrchardCore.Menu
@addTagHelper *, OrchardCore.Contents.TagHelpers
@addTagHelper *, OrchardCore.Media
@addTagHelper *, OrchardCore.DynamicCache
@using OrchardCore.DisplayManagement
@using OrchardCore.DisplayManagement.Shapes
@using OrchardCore.ContentManagement
@using Microsoft.AspNetCore.Html
Guidelines
@inherits OrchardCore.DisplayManagement.Razor.RazorPage<TModel>gives access toOrchard(IOrchardDisplayHelper),New,DisplayAsync,Site,T,ThemeLayout, and the title helpers. Do not@injectanotherOrchardproperty.- Add only the
@addTagHelperlines for assemblies your theme or module actually uses, and reference the matching packages (for exampleOrchardCore.ResourceManagementandOrchardCore.Contents.TagHelpers). @using OrchardCore.DisplayManagement.Razoris not needed when@inheritsalready references that namespace.- No
@using OrchardCoredirective is needed to access content helpers fromIOrchardHelper.
_ViewStart.cshtml Conventions
@{
Layout = "Layout";
}
- Place
_ViewStart.cshtmlin theViewsfolder to set the default layout. "Layout"references the shape namedLayoutwhich resolves toLayout.cshtml(orLayout.liquid) in the active theme.- Modules typically do not set a layout in
_ViewStart.cshtmlbecause their views render inside the theme's layout automatically. - Override per-view by setting
Layout = null;for partial or layoutless pages.
Layout Rendering Patterns
A theme's Layout.cshtml renders the zones of the Layout shape with RenderSectionAsync and the main content with RenderBodyAsync. The <zone> tag helper does the opposite: it adds content to a zone and renders nothing where it is declared.
Minimal Layout Template
<!DOCTYPE html>
<html lang="@Orchard.CultureName()" dir="@Orchard.CultureDir()">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@RenderTitleSegments(Site.SiteName, "before")</title>
<style asp-name="bootstrap" version="5" at="Head"></style>
<style asp-name="{{ThemeStyleName}}" at="Head"></style>
<script asp-name="bootstrap" version="5" at="Foot"></script>
<resources type="Header" />
@await RenderSectionAsync("HeadMeta", required: false)
</head>
<body>
@await RenderSectionAsync("Header", required: false)
<main class="container">
@await RenderSectionAsync("Messages", required: false)
@await RenderBodyAsync()
</main>
@if (IsSectionDefined("Footer"))
{
<footer>
@await RenderSectionAsync("Footer", required: false)
</footer>
}
<resources type="FootScript" />
</body>
</html>
Guidelines
@await RenderBodyAsync()renders theContentzone of the layout.@await RenderSectionAsync("Name", required: false)renders any other zone;IsSectionDefined("Name")checks whether it has content.- Shapes that register resources (navbar, menus, branding) must render before
<resources type="Header" />. Pre-render them into a variable at the top of the layout when they appear after the head, asTheThemedoes with its navbar. <resources type="Header" />rendersMeta,HeadLink,Stylesheet, andHeadScriptin one call. You can also render them individually.- Render
<resources type="FootScript" />right before</body>. RenderTitleSegments(segment, position = "0", separator = null)adds a title segment and returns the full title;"before"places the site name before the page segments.- Do not reference
jQueryimplicitly. Front-end themes that need it must require it explicitly with<script asp-name="jQuery" at="Foot"></script>.
Shape Tag Helpers
Shapes are the fundamental rendering unit in Orchard Core. Use tag helpers to display and compose shapes.
Display a Shape
<shape type="{{ShapeType}}" />
Display a Shape with Properties
<shape type="{{ShapeType}}" prop-title="@item.Title" prop-content="@item.Content" />
- Prefix shape properties with
prop-to pass typed values; plain attributes are passed as strings. - Use
<add-property name="content">...</add-property>inside<shape>to pass Razor markup as anIHtmlContentproperty. - Use
<metadata display-type="Summary"><add-alternate name="..." /><add-wrapper name="..." /></metadata>inside a shape tag helper to adjust metadata.
Display a Shape Object
When you have a shape object (e.g., from a zone or the shape factory), render it with DisplayAsync:
@await DisplayAsync(Model)
@await DisplayAsync(Model.Content)
Cached Shapes
<shape type="{{ShapeType}}" cache-id="{{unique-id}}" cache-fixed-duration="00:05:00" cache-tag="alias:main-menu" cache-context="user.roles" />
Supported cache attributes are cache-id, cache-context, cache-tag, cache-fixed-duration, and cache-sliding-duration. They require the OrchardCore.DynamicCache feature.
Zone Tag Helper
<zone> adds its child content to a layout zone (optionally at a position). Use it in any view to push markup into the layout:
<zone name="Footer" position="after">
<p class="small">@T["Rendered from a content template."]</p>
</zone>
Render the zone in the layout with @await RenderSectionAsync("Footer", required: false).
Common zone names: Header, Navigation, Messages, Content, Sidebar, Footer, HeadMeta, BeforeContent, AfterContent.
Link Generation
In Orchard Core views, prefer MVC tag helpers so link generation stays in the Razor template and remains route-aware.
Preferred View Pattern
<a asp-action="Edit" asp-controller="Admin" asp-area="MyModule" asp-route-id="@Model.ContentItemId">
@T["Edit"]
</a>
Content Item Links
With @addTagHelper *, OrchardCore.Contents.TagHelpers:
<a display-for="@contentItem">@contentItem.DisplayText</a>
<a edit-for="@contentItem">@T["Edit"]</a>
<a admin-for="@contentItem">@T["Admin"]</a>
Guidelines
- Prefer
asp-action,asp-controller, and when neededasp-areainstead of hard-coded application URLs. - Use
display-for/edit-forto link to content items instead of building URLs fromAutoroutePart. - Do not use
@Url.Content("~/...")to generate application links in Orchard Core views. - Continue using normal tag helpers for forms and buttons, such as
<form asp-action="Update" method="post">.
URL Generation in Code
If a URL must be generated outside the view, inject LinkGenerator and pass the current HttpContext so Orchard Core generates the correct route-aware URL.
using Microsoft.AspNetCore.Routing;
public sealed class AdminLinkService
{
private readonly LinkGenerator _linkGenerator;
public AdminLinkService(LinkGenerator linkGenerator)
{
_linkGenerator = linkGenerator;
}
public string? GetEditUrl(HttpContext httpContext, string contentItemId)
{
return _linkGenerator.GetPathByAction(
httpContext,
action: "Edit",
controller: "Admin",
values: new
{
area = "MyModule",
id = contentItemId,
});
}
}
Resource Tag Helpers
Register a Stylesheet
<style asp-name="{{ResourceName}}" at="Head"></style>
Register a Named Resource with a Version
<style asp-name="bootstrap" version="5" at="Head"></style>
Register a Script at the Foot or Head
<script asp-name="{{ResourceName}}" at="Foot"></script>
<script asp-name="{{ResourceName}}" at="Head"></script>
Register an Inline Script
<script at="Foot">
document.addEventListener('DOMContentLoaded', function () {
console.log('Page loaded');
});
</script>
Script Dependencies
<script asp-name="{{ResourceName}}" asp-src="~/{{ThemeName}}/js/site.min.js" debug-src="~/{{ThemeName}}/js/site.js" depends-on="bootstrap:5" at="Foot"></script>
Admin Views Using bootstrap-select
TheAdmin no longer includes bootstrap-select styles automatically. Admin views that use it must require both:
<style asp-name="bootstrap-select" at="Head"></style>
<script asp-name="bootstrap-select" at="Foot"></script>
Render All Registered Resources
Place these in the layout to output all resources registered by shapes and modules:
<resources type="Header" />
<resources type="FootScript" />
type |
Output |
|---|---|
Meta, HeadLink, Stylesheet, HeadScript |
Individual head sections |
Header |
All four head sections |
FootScript |
Scripts registered with at="Foot" |
See orchardcore-resources for the complete resource manager guidance.
Media Tag Helpers
Render an Image from the Media Library
<img asset-src="@imagePath" alt="@altText" />
Resize an Image
<img asset-src="@imagePath" img-width="300" />
<img asset-src="@imagePath" img-width="800" img-height="600" img-resize-mode="Crop" />
| Mode | Description |
|---|---|
Pad |
Resize and pad to fit the target dimensions |
BoxPad |
Pad the image to fit within the bounding box |
Crop |
Resize and crop to fill the target dimensions |
Min |
Resize to the minimum of the target dimensions |
Max |
Resize to the maximum of the target dimensions |
Stretch |
Stretch the image to fill the target dimensions |
See orchardcore-media for the full media tag helper and image processing reference.
Content Tag Helpers
Render a Content Item by Handle
<contentitem alias="alias:{{alias}}" display-type="Summary" />
Render a Loaded Content Item
@await Orchard.DisplayAsync(contentItem, "Summary")
Common Display Types
| Display Type | Description |
|---|---|
Detail |
Full detail view |
Summary |
Abbreviated summary view |
SummaryAdmin |
Admin list row (List layout) |
SummaryAdminGrid |
Admin list card (Grid layout) |
Menu Tag Helper
Render a Named Menu
<menu alias="alias:main-menu" cache-id="main-menu" cache-fixed-duration="00:05:00" cache-tag="alias:main-menu" cache-context="user.roles" />
- The
aliasattribute specifies the alias of the menu content item. - The menu renders using the menu shape templates, which can be overridden in your theme.
Caching Markup
Use Orchard's <dynamic-cache> tag helper (feature OrchardCore.DynamicCache), not ASP.NET Core's <cache>, so entries are tenant-aware and invalidated by Orchard dependencies. Require scripts and styles outside the cached block: code inside it does not run on a cache hit.
<dynamic-cache cache-id="recent-posts" vary-by="route" dependencies="alias:recent-posts" expires-after="00:10:00">
@await DisplayAsync(Model.Content)
</dynamic-cache>
| Attribute | Description |
|---|---|
cache-id |
Required identifier of the cached section |
vary-by |
Space/comma-separated cache contexts (user, user.roles, route, query:page, ...) |
dependencies |
Space/comma-separated invalidation tags (contentitemid:{id}, alias:{alias}) |
expires-after |
Relative expiration |
expires-on |
Absolute DateTimeOffset expiration |
expires-sliding |
Sliding expiration (default 30 seconds when nothing is set) |
Use <cache-dependency dependency="contentitemid:@item.ContentItemId" /> inside a cached block to add dependencies discovered while rendering. See orchardcore-dynamic-cache.
Date-Time Tag Helpers
Render a UTC Date in the Site's Time Zone
<datetime utc="@Model.ContentItem.CreatedUtc" />
Render a Date with a Custom Format
<datetime utc="@Model.ContentItem.PublishedUtc" format="MMMM dd, yyyy" />
Relative Time
<timespan utc="@Model.ContentItem.CreatedUtc" />
IOrchardHelper Extensions
Access IOrchardHelper via the Orchard property in Razor views that inherit from OrchardCore.DisplayManagement.Razor.RazorPage<TModel>.
Content helpers live in ContentOrchardHelperExtensions and are available without adding @using OrchardCore.
Get a Content Item by ID
@{
var item = await Orchard.GetContentItemByIdAsync("{{contentItemId}}");
}
Get Several Content Items by ID
@{
var items = await Orchard.GetContentItemsByIdAsync(ids);
}
Get a Content Item by Version ID
@{
var item = await Orchard.GetContentItemByVersionIdAsync("{{contentItemVersionId}}");
}
Query Content Items
@{
var items = await Orchard.QueryContentItemsAsync(query =>
query.Where(index => index.ContentType == "BlogPost" && index.Published));
}
Get Recent Content Items by Content Type
@{
var items = await Orchard.GetRecentContentItemsByContentTypeAsync("BlogPost", 10);
}
Get a Content Item by Handle
@{
var item = await Orchard.GetContentItemByHandleAsync("alias:{{alias}}");
}
Site Settings
Access global site settings via Site:
<p>@Site.SiteName</p>
<p>@Site.BaseUrl</p>
Log a Value to the Browser Console
@Orchard.ConsoleLog(Model.ContentItem)
Liquid-to-HTML Rendering
@await Orchard.LiquidToHtmlAsync("{{ 'now' | date: '%B %d, %Y' }}")
Only render Liquid that comes from trusted code or from users with the ManageLiquidTemplates permission. HtmlBodyPart, HtmlField, MarkdownBodyPart, and MarkdownField no longer run Liquid in Orchard Core 4.0; use LiquidPart or LiquidField for authored Liquid.
Markdown to HTML
@await Orchard.MarkdownToHtmlAsync(markdown)
@await Orchard.MarkdownToHtmlAsync(markdown, sanitize: false)
The renderLiquid parameter was removed in 4.0.
Defining Named Resources
Register stylesheets and scripts so they can be referenced by name in Razor views. See orchardcore-resources for CDN, integrity, and dependency configuration.
Resource Configuration Class
using Microsoft.Extensions.Options;
using OrchardCore.ResourceManagement;
public sealed class ResourceManagementOptionsConfiguration
: IConfigureOptions<ResourceManagementOptions>
{
private static readonly ResourceManifest _manifest;
static ResourceManagementOptionsConfiguration()
{
_manifest = new ResourceManifest();
_manifest.DefineStyle("{{ThemeStyleName}}")
.SetUrl("~/{{ModuleOrThemeId}}/css/site.min.css", "~/{{ModuleOrThemeId}}/css/site.css")
.SetDependencies("bootstrap:5")
.SetVersion("1.0.0");
_manifest.DefineScript("{{ThemeScriptName}}")
.SetUrl("~/{{ModuleOrThemeId}}/js/site.min.js", "~/{{ModuleOrThemeId}}/js/site.js")
.SetDependencies("bootstrap:5")
.SetVersion("1.0.0");
}
public void Configure(ResourceManagementOptions options)
{
options.ResourceManifests.Add(_manifest);
}
}
Guidelines
- The first
SetUrlparameter is the minified version; the second is the debug version. - Choose where a script renders when you require it (
at="Foot"/at="Head").SetPosition(ResourcePosition.First | Last | ByDependency)only changes ordering, not the head/foot location. - Use
SetDependencies("name")orSetDependencies("name:version")to declare dependencies. - Register the manifest class in
Startup.cs:
public sealed class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddResourceConfiguration<ResourceManagementOptionsConfiguration>();
}
}
Recipe Step for Themes
Set the Active Theme via Recipe
{
"steps": [
{
"name": "Themes",
"Site": "{{ThemeId}}",
"Admin": "TheAdmin"
}
]
}