Layouts and partials can now live in an installed npm package and resolve by package name — so a shared theme ships as a dependency instead of copied files. Works in both Nunjucks and Liquid.
Package templates resolve from node_modules. A layout or partial can now
live in an installed npm package and be referenced by package name, so a
shared theme ships as a dependency instead of files copied into every project.
Anything with a / is resolved from the consumer's node_modules; a bare
name (no /) stays project-only, so the common path never touches the
resolver.
{% extends "my-theme/layout.html" %}
{% block content %}
<h1>{{ page.title }}</h1>
{% endblock %}
Or from front matter, so the page carries no template syntax at all:
---
layout: my-theme/layout
---
require.resolve for pkg/template.html; the Liquid engine adds every
ancestor node_modules on the path to its include roots — so hoisted,
scoped, and pnpm installs all resolve, and liquidjs's containment guard
stays intact.toc, breadcrumb, og, canonical, …
are engine-global, so package templates use them with no extra wiring.A theme package must not restrict subpaths with exports (or must map its
templates explicitly, e.g. "exports": { "./*": "./*" }), and should
reference its own partials relatively — {% import "./nav.html" as nav %},
not the bare name. See Templating HTML → Templates from an npm package.