Security and Scripts

templ’s default posture: everything you interpolate is escaped for the context it lands in, and you opt out explicitly when you know better.

Auto-escaping

Expression output ({ expr } in text and in attribute values) is HTML-escaped automatically. There is no per-site “unsafe” mode to accidentally enable.

The only opt-out is templ.Raw, whose signature makes the intent visible at the call site (runtime.go):

func Raw[T ~string](html T, errs ...error) Component

Warning: templ.Raw disables escaping for its argument. Pass it markup you generated and trust; never raw user input.

Script templates

<script> blocks inside components are script templates. Placeholders written as {{ name }} are escaped for the JavaScript string context, so interpolated values cannot break out of the string and inject code:

templ player(clipID string) {
    <script type="text/javascript">
        function playClip() {
            const id = "{{ clipID }}";
            console.log(id);
        }
    </script>
    <button onClick="playClip()">Play</button>
}

Script templates can also be rendered directly with @, like any other component.

templ.JSONScript

To hand server-side data to client-side JavaScript, use templ.JSONScript. It renders the value into a <script type="application/json"> tag (jsonscript.go):

templ chart(data []float64) {
    @templ.JSONScript("chart-data", data)
    <div id="chart"></div>
}

Client code reads the tag by its id and parses the JSON. The value is encoded server-side, so there is no string concatenation to get wrong.

const data = JSON.parse(document.getElementById("chart-data").textContent);

Further hardening

The security documentation covers three more topics worth reading once your app handles real users:

  • Injection attacks - where untrusted input typically tries to get in
  • Content Security Policy - defense in depth so injected script fails in the browser
  • Code signing - verifying artifacts you distribute

Tip: Prefer script templates and templ.JSONScript over templ.Raw for anything data-shaped. Raw is for trusted markup, not for serializing values.