Asset Management
The Handlebars extension integrates with TYPO3's modern AssetCollector API to manage JavaScript and CSS assets in your frontend rendering. This feature allows you to register external files and inline code directly through TypoScript configuration.
Table of Contents
Overview
TYPO3 13+ provides the Asset service as the recommended way to register
frontend assets. The Handlebars extension fully supports this API through the
assets configuration in HANDLEBARSTEMPLATE content objects.
Benefits
- Deduplication: Assets with the same identifier are automatically merged across the page
- Priority Control: Control rendering order with priority options
- CSP Support: Automatic nonce injection for Content Security Policy compliance
- Modern API: Uses TYPO3's recommended approach (not deprecated PageRenderer methods)
Asset Types
The AssetCollector API supports four distinct asset types, all fully supported by this extension:
- External JavaScript Files - Link to external .js files
- Inline JavaScript Code - Embed JavaScript directly in the page
- External CSS Files - Link to external .css files
- Inline CSS Code - Embed styles directly in the page
JavaScript Files
Register external JavaScript files using the java configuration:
10 = HANDLEBARSTEMPLATE
10 {
templateName = MyTemplate
assets {
javaScript {
my-app-script {
source = EXT:myext/Resources/Public/JavaScript/app.js
attributes {
async = 1
defer = 1
crossorigin = anonymous
}
options {
priority = 1
useNonce = 1
}
}
}
}
}
Inline JavaScript
Add inline JavaScript code using inline:
assets {
inlineJavaScript {
my-inline-script {
source = console.log('Hello from Handlebars'); initMyApp();
attributes {
type = module
}
options {
priority = 1
}
}
}
}
CSS Files
Register external stylesheets using the css configuration:
assets {
css {
my-styles {
source = EXT:myext/Resources/Public/Css/styles.css
attributes {
media = screen and (max-width: 768px)
}
options {
priority = 1
}
}
}
}
Inline CSS
Add inline styles using inline:
assets {
inlineCss {
critical-css {
source = body { margin: 0; padding: 0; } .container { max-width: 1200px; }
}
}
}
Configuration Reference
source (required)
- Type
- string
- Description
-
Asset source. For external files, use
EXT:syntax or absolute paths. For inline assets, provide the code directly as a string value.Note
The source must be a direct string value. Dynamic asset sources via stdWrap are not supported. Use fixed paths or inline code only.
- Example
# External JavaScript file source = EXT:myext/Resources/Public/JavaScript/file.js # External CSS file source = EXT:myext/Resources/Public/Css/styles.css # Inline JavaScript code source = console.log('Hello'); # Inline CSS code source = body { margin: 0; }Copied!
attributes
- Type
- array
- Description
- HTML attributes for the generated tag. Boolean attributes (
async,defer,disabled) should be set to1to enable them. - JavaScript Attributes
-
async(boolean): Load script asynchronouslydefer(boolean): Defer script executionnomodule(boolean): Fallback for older browserstype(string): Script type (e.g., "module")crossorigin(string): CORS setting (e.g., "anonymous")integrity(string): Subresource Integrity hash
- CSS Attributes
-
media(string): Media query (e.g., "screen", "print")disabled(boolean): Disable stylesheettitle(string): Stylesheet titlecrossorigin(string): CORS settingintegrity(string): Subresource Integrity hash
- Example
attributes { async = 1 defer = 1 type = module crossorigin = anonymous integrity = sha384-abc123def456 }Copied!
options
- Type
- array
- Description
- AssetCollector-specific options that control asset rendering behavior.
- Available Options
-
priority(boolean): Render before other assets (default: 0)use(boolean): Add CSP nonce attribute (default: 0)Nonce
- Example
options { priority = 1 useNonce = 1 }Copied!
Complete Examples
Basic Example
page.20 = HANDLEBARSTEMPLATE
page.20 {
templateName = MyPage
variables {
title = TEXT
title.data = page:title
}
assets {
# External JavaScript
javaScript {
app-script {
source = EXT:myext/Resources/Public/JavaScript/app.js
attributes {
defer = 1
}
}
}
# External CSS
css {
main-styles {
source = EXT:myext/Resources/Public/Css/main.css
}
}
}
}
Advanced Example with Priority
page.20 = HANDLEBARSTEMPLATE
page.20 {
templateName = MyPage
assets {
# High-priority critical CSS
css {
critical-styles {
source = EXT:myext/Resources/Public/Css/critical.css
options {
priority = 1
}
}
}
# Regular stylesheet with media query
css {
responsive-styles {
source = EXT:myext/Resources/Public/Css/responsive.css
attributes {
media = screen and (min-width: 768px)
}
}
}
# Inline critical CSS (highest priority)
inlineCss {
above-the-fold {
source = body { font-family: sans-serif; } h1 { font-size: 2em; }
options {
priority = 1
}
}
}
# Modern JavaScript module
javaScript {
app-module {
source = EXT:myext/Resources/Public/JavaScript/app.js
attributes {
type = module
}
options {
useNonce = 1
}
}
}
# Legacy fallback for older browsers
javaScript {
app-legacy {
source = EXT:myext/Resources/Public/JavaScript/app.legacy.js
attributes {
nomodule = 1
defer = 1
}
}
}
# Inline initialization code
inlineJavaScript {
app-init {
source = window.APP_CONFIG = { apiUrl: '/api' };
options {
priority = 1
}
}
}
}
}
CDN Example with Security
assets {
javaScript {
cdn-library {
source = https://cdn.example.com/library.js
attributes {
crossorigin = anonymous
integrity = sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC
defer = 1
}
}
}
css {
cdn-styles {
source = https://cdn.example.com/styles.css
attributes {
crossorigin = anonymous
integrity = sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN
}
}
}
}
Migration from Legacy Methods
If you're using the legacy header or footer
configuration, consider migrating to the modern assets approach:
Before (Legacy)
10 = HANDLEBARSTEMPLATE
10 {
templateName = MyTemplate
headerAssets = TEXT
headerAssets.value = <script src="path/to/script.js"></script>
footerAssets = TEXT
footerAssets.value = <script>console.log('footer');</script>
}
After (Modern)
10 = HANDLEBARSTEMPLATE
10 {
templateName = MyTemplate
assets {
javaScript {
my-script {
source = EXT:myext/Resources/Public/JavaScript/script.js
}
}
inlineJavaScript {
footer-script {
source = console.log('footer');
}
}
}
}
Benefits of Migration
- Automatic asset deduplication across multiple content elements
- Better control over rendering order via priority
- Content Security Policy nonce support
- Type-safe attribute handling
- Future-proof implementation using TYPO3's recommended API
Note
Legacy methods (header and footer)
remain fully supported for backward compatibility. You can use both modern
and legacy methods in the same content object.
Best Practices
Unique Identifiers
Always use unique, namespaced identifiers across your entire page to avoid conflicts:
# Good: Namespaced identifier
my-extension-app-script {
source = ...
}
# Bad: Generic identifier (may conflict with other extensions)
app {
source = ...
}
Priority Management
Use priority = 1 for assets that must load early:
# Critical inline CSS should have priority
inlineCss {
critical {
source = ...
options.priority = 1
}
}
# Regular stylesheets can have normal priority
css {
theme {
source = ...
# No priority option = loaded after priority assets
}
}
Content Security Policy
Enable use for inline scripts when using CSP:
inlineJavaScript {
inline-config {
source = window.config = { /* ... */ };
options.useNonce = 1
}
}
Module vs Classic Scripts
Use type = module for ES6 modules and provide nomodule
fallback for older browsers:
# Modern browsers
javaScript {
app-module {
source = EXT:myext/Resources/Public/JavaScript/app.module.js
attributes.type = module
}
}
# Legacy browsers
javaScript {
app-legacy {
source = EXT:myext/Resources/Public/JavaScript/app.legacy.js
attributes.nomodule = 1
}
}
Troubleshooting
Assets Not Appearing
If assets don't appear in the rendered page:
- Check identifiers are unique - Duplicate identifiers will cause the last one to win
- Verify source is not empty - Empty sources will throw an exception
- Check for exceptions - Configuration errors will halt rendering and display an error
- Ensure AssetCollector is initialized - Only works in frontend context
Duplicate Assets
If assets appear multiple times, check for:
- Duplicate identifiers in different content objects
- Multiple
HANDLEBARSTEMPLATEobjects registering the same asset
Use unique, namespaced identifiers to prevent conflicts:
# Use extension prefix to avoid conflicts
my-ext-my-script {
source = ...
}
Configuration Errors
The extension throws Invalid for:
- Unknown asset type (valid types:
java,Script inline,Java Script css,inline)Css - Invalid asset type configuration (must be an array)
- Invalid or empty identifier
- Invalid asset configuration (must be an array)
- Missing
sourceparameter - Empty
sourcevalue (after trimming whitespace)
These errors will halt page rendering and display an exception message. Check your TypoScript configuration to resolve the issue.
Boolean Attributes Not Working
Boolean attributes require the value 1 (or any truthy value) to be enabled:
# Correct
attributes {
async = 1 # Will output: async="async"
defer = 1 # Will output: defer="defer"
}
# Incorrect - will not be output
attributes {
async = 0 # Omitted from output
defer = # Omitted from output
}