Area: Episerver B2B Commerce

Migrating from Razor to DotLiquid in Classic

Recommended reading 

This article provides some basic help when migrating existing Razor views into DotLiquid views. This includes some regular expressions that can be run in Visual Studio to do some of the tedious work required for larger views.

Basic Razor Variables

When migrating an existing Razor view to DotLiquid all @Model references need to be replaced. In general, anything of the form @Model.Variable can be converted to [[Model.Variable]].

Regex to change these in VS

Find: @\(?Model\.([\w\.]+)\)?

Replace: [[Model.$1]]

Basic Control Structures

@if can be converted to [% if %] [% else %] [% endif %]

@foreach can be converted to [% for x in Y %] [% endfor %]

When doing blocks like if and for, ending them with -%] will cause them to not be a blank link in the rendered HTML.

See GitHub for more information about the basic DotLiquid syntax.

If there is complicated logic in the Razor view, it will need to be moved into the ContentItemModel.

If any properties on the ContentItemModel are not basic types, they need to return something that derives from Drop, or a list of something that derives from Drop.


@Html.Zone(Model, "[ZoneName]") can be converted to [% zone 'ZoneName' %]


@Url.ContentPage<[PageType]>() can be converted to [% urlForPage '[PageType]' %]

Regex to change these in VS

Find: @\(?Url\.ContentPage<(.*)>\(\)\)?

Replace: [% urlForPage '$1' %]

@Url.Action needs to be converted into the real URL, and then [% urlFor '[url]' %] - this will take microsites into account.


@Html.Message("[MessageName]", "[DefaultValue]") can be converted to [% siteMessage '[MessageName]' %]

When doing this, ensure that MessageProvider contains a property [MessageName], if it does not, add it, and move the default value from the razor view into the MessageProvider property

Regex to change these in VS

Find: @Html\.Message\("(.*?)",.*?\)

Replace: [% siteMessage '$1' %]

Find: @MessageProvider\.Current\.([a-zA-Z_-]*)

Replace: [% siteMessage '$1' %]

If any have site messages, translate, and a string format.

Find: @string\.Format\(MessageProvider\.Current\.([a-zA-Z_-]*), T\("(.*?)"\)\)

Replace: {{::'[% siteMessage '$1' %]'.replace('{0}', '[% translate '$2' %]')}}


@T("Translated String") can be converted to [% translate 'Translated String' %]

NOTE: Some translations have an extra pair of parentheses @(T("Translated String")) wrapping the call to the translation function. The regex handles that as well.

Regex to change these in VS

Find: @\(?T\("(.*?)"\)\)?

Replace: [% translate '$1' %]

Themed Partials

@Html.ThemedPartial("/Views/Directives/Folder/File.cshtml") can be converted to [% translate 'Folder-file' %]

Regex to change these in VS

Find: @Html\.ThemedPartial\("/Views/Directives/([a-zA-Z_-]*)/([a-zA-Z_-]*)\.cshtml"\)

Replace: [% partialView '$1-$2' %]


@Html.Action("GenericPopupPartial", "Shared", new { containerName = "popup-deletewishlist", BodyHtml = Model.DeleteWishlistMessage, Title = @T("Delete Wishlist") })

Needs to be switched to:

<isc-popup-template title="[% translate 'Delete Wishlist'%]" container-id="popup-deletewishlist">



Razor Helpers

@helpers can sometimes be converted into captures, as long as they aren't passed in parameters.

Do you find this information helpful? Please log in to provide feedback.

Last updated: Dec 11, 2020

Recommended reading