Elegant asset management for PHP with versioning, caching and providers for various storage backends
Nette Assets is a powerful asset management library for PHP that helps you:
✅ organize and serve your static assets (images, CSS, JavaScript, audio, etc.)
✅ handle asset versioning automatically
✅ get image dimensions without hassle
✅ verify asset existence
✅ support multiple storage backends
✅ integrate with Vite for modern frontend development
The library provides a clean and intuitive API to manage static assets in your web applications with a focus on developer experience and performance.
The recommended way to install is via Composer:
composer require nette/assets
Nette Assets requires PHP 8.1 or higher.
The library revolves around a few key components:
__toString()
. Different asset types (like ImageAsset
, ScriptAsset
, AudioAsset
) provide type-specific properties.app.js
or images/logo.png
) and resolving it into an Asset
object. Different mappers can fetch assets from various sources (filesystem, CDN, cloud storage, manifest files). FilesystemMapper
is the built-in implementation for serving files from a local directory. If the requested asset cannot be found, the mapper throws an AssetNotFoundException
.Mapper
instances, each identified by a unique string ID (e.g., 'default'
, 'audio'
, 'images'
). It provides the main entry point (getAsset()
) for retrieving assets using a qualified reference, which throws AssetNotFoundException
if the requested asset cannot be found. For cases where handling non-existent assets without exceptions is preferred, it also provides tryGetAsset()
, which returns null
instead of throwing an exception.Registry
. It supports three formats:
reference
(e.g., 'app.js'
) which uses the default
mapper.mapper:reference
(e.g., 'audio:podcast.mp3'
) which specifies the mapper explicitly.[mapper, reference]
(e.g., ['images', 'logo.png']
) which also specifies the mapper explicitly.The library provides specialized asset interfaces and implementations for different content types:
Configuration is typically done in your application’s NEON configuration file under the assets
key.
You can define a base filesystem path and URL prefix under the main assets:
key. These serve as the foundation from which relative paths defined in mappers are resolved.
assets:
path: %wwwDir%/static
url: /static
However, explicit configuration is often optional. If omitted, path
typically defaults to your public web root (%wwwDir%
), and url
defaults to the application’s base URL path (for example https://domain/
).
The mapping
section defines your named mappers. Each key in mapping
is a mapper identifier (e.g., default
, audio
, images
). Now, let’s see how different mapper configurations under mapping
behave relative to the base settings:
assets:
# base settings
path: %wwwDir%/static
url: /static
mapping:
default: assets # path becomes '%wwwDir%/static/assets', URL becomes '/static/assets'
images:
path: img # path becomes '%wwwDir%/static/img'
url: /images # URL becomes '/images' (because of absolute path)
cdn_styles:
path: /var/www/shared/readonly # this absolute path is used
url: https://cdn.example.com/css/ # this absolute URL is used
# Vite integration
vite:
type: vite # specifies ViteMapper
manifestPath: dist/manifest.json # path to Vite's manifest file
baseUrl: /dist # base URL for Vite assets
extension: [js, mjs, css] # auto-extensions to try
devServerUrl: http://localhost:5173 # URL to Vite dev server (optional)
debug: %debugMode% # enable dev mode based on app's debug flag
The mapper can automatically handle file extensions if the reference doesn’t include one. You configure this using the extension
option within a mapper’s definition.
assets:
mapping:
# Always adds '.css', 'styles:main' becomes 'styles/main.css'
styles:
path: styles
extension: css # No leading dot
# Tries '.svg' and then '.png' extensions, 'icons:myicon' becomes 'img/icons/myicon.svg' or 'img/icons/myicon.png'
icons:
path: img/icons
extension: [svg, png] # Order matters
# Adds '.js' if missing, both 'scripts:app' and 'scripts:app.js' become 'js/app.js'
scripts:
path: js
extension: [js, ''] # Empty string allows matching without adding an extension
By default, the configurations in the mapping
section implicitly create instances of the built-in Nette\Assets\FilesystemMapper
. If FilesystemMapper
doesn’t fit your needs (e.g., you need to load assets from a database, S3, or read a manifest file generated by build tools like Vite or Webpack), you can provide an instance of your own class implementing Nette\Assets\Mapper
:
assets:
mapping:
# Uses a custom class instance directly
products: App\Inventory\ProductImageMapper
# Or references a service defined elsewhere in your configuration
vite: @App\Build\ViteManifestMapper(%appDir%/../dist/.vite/manifest.json)
Retrieve assets via the Registry
service, typically injected where needed. The main method is getAsset()
:
// Assume $assets is Nette\Assets\Registry obtained via dependency injection or service locator
// Option 1: Using getAsset() with try/catch for handling exceptions
$reference = 'images:logo.png'; // Or ['images', 'logo.png'], or just 'logo.png' for default mapper
try {
$asset = $assets->getAsset($reference);
echo $asset->url;
} catch (Nette\Assets\AssetNotFoundException $e) {
// Handle asset not found situation
echo 'Asset not found: ' . $e->getMessage();
}
// Option 2: Using tryGetAsset() for nullable return value
$asset = $assets->tryGetAsset($reference);
echo $asset?->url;
Assuming Latte helper asset
is registered to call the Registry
:
{* Using the default mapper (mapper identifier omitted) *}
<script src={asset('app.js')}></script>
<link rel="stylesheet" href={asset('style.css')}>
{* Specifying the mapper using the prefix *}
<audio src={asset('audio:podcast.mp3')}></audio>
<img src={asset('icons:logo')} alt="Logo">
{* Alternative syntax using an array *}
<audio src={asset(['audio', 'podcast.mp3'])}></audio>
{* Using tryAsset() for asset which may not exist *}
{var $asset = tryAsset('images:optional.jpg')}
<img n:if=$asset src=$asset alt="Optional Image">
The resulting URL string obtained from $asset->url
or {asset(...)}
will include versioning information if provided by the mapper.
The built-in FilesystemMapper
supports asset versioning. By default, it automatically appends a version query parameter based on the file’s last modification time (filemtime
):
{asset('app.js')}
generates, for example:
/assets/app.js?v=1699944800
This helps with browser cache invalidation. Custom mappers can implement different versioning strategies (e.g., using content hashes from a build manifest).
You can disable versioning per asset:
$asset = $mapper->getAsset('app.js', ['versioning' => false]);
Or in configuration file:
assets:
# Global versioning setting (defaults to true)
versioning: false
mapping:
default:
path: assets
# Disable versioning for this mapper only
versioning: false
When using FilesystemMapper
(or any mapper returning a FileAsset
), you can easily retrieve image dimensions (assuming corresponding Latte helpers are registered):
<img src={asset('images:logo.png')} width={asset('images:logo.png')->width} height={asset('images:logo.png')->height}>
{* alternative *}
{do $asset = asset('images:logo.png')}
<img src={$asset} width={$asset->width} height={$asset->height}>
FileAsset
provides $duration
property for estimating MP3 duration (most reliable for Constant Bitrate files).
Nette Assets supports modern bundlers and build tools that generate multiple files from a single entry point:
EntryAsset
class implements both ScriptAsset
and StyleAsset
interfacesrenderAsset
Latte function automatically generates HTML tags for all related filesVite integration automatically handles:
{* Renders all necessary script and link tags for this entry point *}
{renderAsset('vite:src/main.js')}