Skip to content
Alexander Selishchev edited this page Oct 22, 2026 · 13 revisions

By default, RazorEngine does not encode values to be HTML safe, and there are reasons for that. This means:

  • you need to escape values yourself
  • you need to implement the @Raw helper yourself

But don't worry, this page is here to help.

Default behavior

IRazorEngine razorEngine = new RazorEngine();
IRazorEngineCompiledTemplate template = razorEngine.Compile("Hello @Model.Name");

string result = template.Run(new
{
    Name = "<b>Test</b>"
});

Console.WriteLine(result);

will output

Hello <b>Test</b>

HtmlSafeTemplate

RazorEngine razorEngine = new RazorEngine();

var razorEngineCompiledTemplate = razorEngine.Compile<HtmlSafeTemplate>(
    "<div title=\"@Model.FirstName\">This is now safe: @Model.FirstName</div>\n" +
    "<div>but not this: @Raw(Model.FirstName)</div>");

string result = razorEngineCompiledTemplate.Run(instance =>
{
    instance.Model = new AnonymousTypeWrapper(new
    {
        FirstName = "<script>alert(\"1\");</script>",
        LastName = "123"
    });
});

Console.WriteLine(result);

Output:

<div title="&lt;script&gt;alert(&quot;1&quot;);&lt;/script&gt;">This is now safe: &lt;script&gt;alert(&quot;1&quot;);&lt;/script&gt;</div>
<div>but not this: <script>alert("1");</script></div>

Code NET5+

using System.Text.Encodings.Web;
using System.Text.Unicode;

public class HtmlSafeTemplate : RazorEngineTemplateBase
{
    // HtmlEncoder.Default escapes everything outside Basic Latin (Cyrillic, CJK etc.) as &#x...;
    private static readonly HtmlEncoder Encoder = HtmlEncoder.Create(UnicodeRanges.All);

    class RawContent
    {
        public object Value { get; set; }

        public RawContent(object value)
        {
            Value = value;
        }
    }


    public object Raw(object value)
    {
        return new RawContent(value);
    }

    public override void Write(object obj = null)
    {
        object value = obj is RawContent rawContent
            ? rawContent.Value
            : Encoder.Encode(obj?.ToString() ?? string.Empty);

        base.Write(value);
    }

    public override void WriteAttributeValue(string prefix, int prefixOffset, object value, int valueOffset, int valueLength, bool isLiteral)
    {
        // literal parts of attribute come from the template as is and may already contain entities like &amp;
        if (!isLiteral)
        {
            value = value is RawContent rawContent
                ? rawContent.Value
                : Encoder.Encode(value?.ToString() ?? string.Empty);
        }

        base.WriteAttributeValue(prefix, prefixOffset, value, valueOffset, valueLength, isLiteral);
    }
}

Code NET4.7.2

public class HtmlSafeTemplate : RazorEngineTemplateBase
{
    class RawContent
    {
        public object Value { get; set; }

        public RawContent(object value)
        {
            Value = value;
        }
    }


    public object Raw(object value)
    {
        return new RawContent(value);
    }

    public override void Write(object obj = null)
    {
        object value = obj is RawContent rawContent
            ? rawContent.Value
            : System.Web.HttpUtility.HtmlEncode(obj);

        base.Write(value);
    }

    public override void WriteAttributeValue(string prefix, int prefixOffset, object value, int valueOffset, int valueLength, bool isLiteral)
    {
        // literal parts of attribute come from the template as is and may already contain entities like &amp;
        if (!isLiteral)
        {
            value = value is RawContent rawContent
                ? rawContent.Value
                : System.Web.HttpUtility.HtmlAttributeEncode(value?.ToString());
        }

        base.WriteAttributeValue(prefix, prefixOffset, value, valueOffset, valueLength, isLiteral);
    }
}

Notes

  • Only values written with @ are encoded. Literal text of the template, including literal parts of attributes like href="/search?a=1&amp;q=@Model.Q", is written as is.
  • HTML encoding is not suitable for JavaScript: in onclick="go('@Model.Q')" the browser decodes &#x27; back to ' before running the script (so the value can break out of the string), and inside <script> entities are not decoded at all. Use JavaScriptEncoder or pass data via data-* attributes for such cases.
  • Characters outside the Basic Multilingual Plane (emoji etc.) are always written as &#x1F600; entities, browsers render them correctly.

Clone this wiki locally