JavaScript Templates
Using JavaScript files in the templates directory with template directives
JavaScript files placed in the templates/ directory are processed through the Elements template engine, allowing you to inject component properties, conditionals, and dynamic values directly into your JavaScript code. This enables you to create interactive components with configuration driven by user properties.
Overview
JavaScript template files provide:
Property insertion - Use component properties in JavaScript code
Conditional code - Include or exclude JavaScript based on conditions
Per-instance configuration - Each component instance gets its own configuration
Portal injection - Place scripts in specific page locations using
@portal(from an HTML template)
File Location
com.yourcompany.component/
├── templates/
│ ├── index.html
│ ├── alpine.html # Portal-wrapped <script> (core convention)
│ ├── script.js # Bare JavaScript template (also supported)
│ └── include/
│ └── ...Basic Usage
JavaScript files can use standard JavaScript syntax along with Elements template directives:
Property Insertion
Insert component properties directly into JavaScript values:
Portal Injection
A common pattern is to use @portal to inject JavaScript into specific page locations, typically bodyEnd. Because the block contains <script> markup, it belongs in an .html template file (such as templates/alpine.html), not a .js file:
Then in your HTML template:
See the @portal documentation for more details.
Examples from Core Components
Accordion Component (Alpine.js)
The Accordion's templates/alpine.html uses a portal to define the Alpine.js component logic once:
Each accordion instance then uses this component with its own properties:
Gallery Component (Lightbox)
The Gallery registers its lightbox as an Alpine.js factory in a root-level template, templates/alpine-gallery-lightbox.html:
The lightbox markup in templates/include/lightbox.html then binds each instance to the factory:
Note the portal target can be written quoted (@portal("bodyEnd", ...)) or bare (@portal(bodyEnd, ...)) — both forms appear in the core components.
Conditional JavaScript
Use @if directives to conditionally include code. Template @if accepts a single condition only — boolean properties like enableLogging work directly, but comparisons must be computed in hooks.js first. See Combining Conditions.
Processing Behavior
Per-Instance Processing
JavaScript template files are processed once per component instance. Each instance gets its own <script> tag with processed code:
Inline Scripts
Processed JavaScript is inserted inline into the page within <script> tags, not as external files.
Templates vs Assets
Choose the right location for your JavaScript:
Use templates/ JavaScript When:
Code needs component property values
Code is instance-specific
Code needs conditional logic
Configuration varies per instance
Example:
Use assets/ JavaScript When:
Code is static library code
Code is shared across all instances
Code is a large framework (Alpine.js, GSAP, etc.)
You want better caching and performance
Example:
Combining Both
A common pattern is to define classes in assets/ and instantiate them in templates/:
Best Practices
Use includeOnce for Libraries
When defining reusable functions or classes, use includeOnce:
Avoid Inline Event Handlers
Instead of inline handlers in HTML:
Use proper event listeners in JavaScript:
Escape String Values
Be careful with user-provided strings that might contain quotes:
In hooks.js:
Minimize Template JavaScript
Keep template JavaScript minimal and focused on configuration:
Use Proper Scope
Avoid polluting global scope:
Handle Timing
Ensure DOM is ready before accessing elements:
Common Patterns
Alpine.js Integration
Define Alpine components with property-based configuration:
Registering inside alpine:init is required for browser/publish parity. In the edit canvas Alpine may already be initialised when the script runs, so a bare Alpine.data(...) call can appear to work — but in a browser the factory must exist before Alpine parses x-data attributes, which means it must be registered before alpine:init fires. Always use this pattern.
Don't interpolate raw JSON into the x-data expression (e.g. x-data="factory('{{id}}', {{dataJson}})" where dataJson is plain JSON.stringify output). The JSON's double-quotes terminate the HTML attribute, Alpine fails to parse the expression, and the component silently renders blank. Two working patterns:
Data attributes (recommended) — pass the JSON through a single-quoted
data-*attribute and read it withJSON.parsein the factory. See Data Attributes.Quote-swap — used by the core Table and Content Slider components: swap the JSON's double-quotes for single-quotes in
hooks.jsbefore interpolating. See below.
The Quote-Swap Pattern
The Content Slider builds its Swiper config in hooks.js and swaps the JSON's double-quotes for single-quotes so the value survives inside the double-quoted x-data attribute:
This works because Alpine evaluates x-data as a JavaScript expression, and the single-quoted output ({'loop': true, ...}) is a valid object literal. The Table component defensively handles a string arriving instead, converting the quotes back before parsing:
The caveat: the quote-swap corrupts data that itself contains quote characters, so reserve it for config objects whose values you control. For user-entered content, use a data-* attribute instead.
GSAP Animations
Configure GSAP animations with user properties:
Data Attributes
Set data attributes for JavaScript access:
Passing structured data (objects/arrays) from hooks.js
For structured data, serialise it in hooks.js and embed it in a single-quoted data-* attribute. A single-quoted attribute keeps JSON's double-quotes intact; putting them inside a double-quoted x-data attribute breaks the HTML parse.
Because {{ }} inserts values verbatim (see Escape String Values), entity-encode ', &, and < in hooks.js if the data may contain those characters.
Limitations
No ES Modules
Template JavaScript doesn't support ES module syntax:
Limited Preprocessing
Template JavaScript files don't support TypeScript, Babel, or other preprocessors. Use the Elements template language for logic.
Execution Context
Template JavaScript runs in the page context, not in a controlled environment. Ensure code is safe and handles errors appropriately.
Related Documentation
Elements Language - Template syntax reference
@portalDirective - Injecting scripts to page locationsTemplates Overview - Understanding templates
Assets - Static JavaScript files
Hooks.js - Preparing data for JavaScript
Interactive Components with Alpine.js - The full guide to Alpine-driven components
Integrating JavaScript Libraries - Shipping third-party libraries with components
Last updated
Was this helpful?

