Skip to content

twig.js Compatibility

The addon ships several shims and compile-time patches that make PHP Twig templates work in the browser via twig.js 1.x.

Automatic patches

These are applied automatically — no configuration needed.

random() shim

twig.js does not implement random(). The addon registers a shim via extendFunction that matches PHP Twig's behavior:

ArgumentReturns
random(n)Random integer between 0 and n (inclusive)
random(array)Random element from the array
random(string)Random character from the string
random()Random 7-character alphanumeric string

Commonly used in patterns to generate unique DOM IDs for accordion, tabs, and modal components.

Arrow-function |filter

PHP Twig 3.x added arrow-function syntax to |filter:

twig
{{ items|filter(v => v) }}

twig.js cannot parse =>. The addon preprocesses templates before registration, replacing |filter(v => v) with a custom |filter_truthy filter that removes falsy values from arrays.

function('php_fn')

PHP Twig's function() calls a PHP function by name at render time. twig.js looks for a registered function called function (the literal word), which doesn't exist, and throws an error.

The addon preprocesses templates to replace function('any_name') with '' (empty string), which allows surrounding |default(...) filters to activate correctly.

Short-ternary patch

twig.js 1.x has a runtime bug with short ternaries (a ? b without an else branch). The operator always pops 3 values from the stack, but a short ternary only pushes 2, causing either a crash ("Expected end of array") or a silently wrong value.

The addon applies a two-part patch via Twig.extend():

  • Compile-time: marks ? tokens as short-ternary when no : follows
  • Runtime: pops only 2 values for short-ternary tokens

{% import _self %} macro hoisting

PHP Twig compiles macros before any output code runs, so they're available throughout a template regardless of source order. twig.js evaluates tokens sequentially — if {% import _self as X %} appears before the macro definitions, X is an empty object.

The addon hoists all {% macro %}...{% endmacro %} blocks to the top of any template that uses {% import _self %}, ensuring macros are registered before the import runs.

source() function

PHP Twig's source() reads a file and returns its raw content. The addon implements it as:

  1. Checks window.__svgSourceRegistry — a build-time registry populated via import.meta.glob in your preview.ts (useful for inline SVGs)
  2. Falls back to a synchronous XHR request for paths served via Storybook's staticDirs
  3. Returns '' on error (network error, cross-origin, 404) — no throw

For SVG spritemap use, populate the registry in your preview.ts:

ts
// .storybook/preview.ts
const svgFiles = import.meta.glob('/path/to/svg/*.svg', { query: '?raw', eager: true });
(window as any).__svgSourceRegistry = Object.fromEntries(
  Object.entries(svgFiles).map(([path, mod]) => [
    `/svg/${path.split('/').pop()}`,
    (mod as { default: string }).default
      .replace(/<\?xml[^?]*\?>/g, '')
      .replace(/<!--[\s\S]*?-->/g, '')
      .trim(),
  ])
);

Known limitations

  • {% apply %} tag — not supported by twig.js. Avoid in pattern templates.
  • Timber-specific filters (|timber_image, |get_src_from_attachment_id, etc.) — these are PHP-only and will throw errors if called in templates. Guard with {% if image %} checks or use |default.
  • PHP functions via function() — only no-argument function('name') calls are patched. Complex expressions like function('fn', arg) are not handled.
  • {% cache %}/{% spaceless %} — twig.js 1.x does not support all PHP Twig 3.x tags. Test patterns that use unusual tags in the browser.

Template naming requirement

Only files named _*.tpl.twig (leading underscore) are registered in the template registry by default. The leading underscore marks the file as an embeddable partial rather than a standalone demo page. Override with the templateGlob option if your project uses a different naming convention.

Released under the MIT License.