Nette Assets

Sorry, page is not translated yet.

Tired of manually managing static files in your web applications? Forget about hardcoding paths, dealing with cache invalidation, or worrying about file versioning. Nette Assets transforms how you work with images, stylesheets, scripts, and other static resources.

  • Smart versioning ensures browsers always load the latest files
  • Automatic detection of file types and dimensions
  • Seamless Latte integration with intuitive tags
  • Flexible architecture supporting filesystems, CDNs, and Vite
  • Lazy loading for optimal performance

Why Nette Assets?

Working with static files often means repetitive, error-prone code. You manually construct URLs, add version parameters for cache busting, and handle different file types differently. This leads to code like:

<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">

With Nette Assets, all this complexity disappears:

{* Everything automated - URL, versioning, dimensions *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">

{* Or just *}
{asset 'css/style.css'}

That's it! The library automatically:

  • Adds version parameters based on file modification time
  • Detects image dimensions and includes them in the HTML
  • Generates the correct HTML element for each file type
  • Handles both development and production environments

Installation

Install Nette Assets using Composer:

composer require nette/assets

It requires PHP 8.1 or higher and works perfectly with Nette Framework, but can also be used standalone.

First Steps

Nette Assets works out of the box with zero configuration. Place your static files in the www/assets/ directory and start using them:

{* Display an image with automatic dimensions *}
{asset 'logo.png'}

{* Include a stylesheet with versioning *}
{asset 'style.css'}

{* Load a JavaScript module *}
{asset 'app.js'}

For more control over the generated HTML, use the n:asset attribute or the asset() function.

How It Works

Nette Assets is built around three core concepts that make it powerful yet simple to use:

Assets – Your Files Made Smart

An asset represents any static file in your application. Each file becomes an object with useful readonly properties:

$image = $assets->getAsset('photo.jpg');
echo $image->url;      // '/assets/photo.jpg?v=1699123456'
echo $image->width;    // 1920
echo $image->height;   // 1080
echo $image->mimeType; // 'image/jpeg'

Different file types provide different properties:

  • Images: width, height, alternative text, lazy loading
  • Scripts: module type, integrity hashes, crossorigin
  • Stylesheets: media queries, integrity
  • Audio/Video: duration, dimensions
  • Fonts: proper preloading with CORS

The library automatically detects file types and creates the appropriate asset class.

Mappers – Where Files Come From

mapper knows how to find files and create URLs for them. You can have multiple mappers for different purposes – local files, CDN, cloud storage, or build tools (each of them has a name). The built-in FilesystemMapper handles local files, while ViteMapper integrates with modern build tools.

Mappers are defined in the configuration.

Registry – Your Main Interface

The registry manages all mappers and provides the main API:

// Inject the registry in your service
public function __construct(
	private Nette\Assets\Registry $assets
) {}

// Get assets from different mappers
$logo = $this->assets->getAsset('images:logo.png'); // 'image' mapper
$app = $this->assets->getAsset('app:main.js'); // 'app' mapper
$style = $this->assets->getAsset('style.css'); // uses default mapper

The registry automatically selects the right mapper and caches results for performance.

Working with Assets in PHP

The Registry provides two methods for retrieving assets:

// Throws Nette\Assets\AssetNotFoundException if file doesn't exist
$logo = $assets->getAsset('logo.png');

// Returns null if file doesn't exist
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
	echo $banner->url;
}

Specifying Mappers

You can explicitly choose which mapper to use:

// Use default mapper
$file = $assets->getAsset('document.pdf');

// Use specific mapper with prefix
$image = $assets->getAsset('images:photo.jpg');

// Use specific mapper with array syntax
$script = $assets->getAsset(['scripts', 'app.js']);

Asset Properties and Types

Each asset type provides relevant readonly properties:

// Image properties
$image = $assets->getAsset('photo.jpg');
echo $image->width;     // 1920
echo $image->height;    // 1080
echo $image->mimeType;  // 'image/jpeg'

// Script properties
$script = $assets->getAsset('app.js');
echo $script->type;     // 'module' or null

// Audio properties
$audio = $assets->getAsset('song.mp3');
echo $audio->duration;  // duration in seconds

// All assets can be cast to string (returns URL)
$url = (string) $assets->getAsset('document.pdf');

Properties like dimensions or duration are loaded lazily only when accessed, keeping the library fast.

Using Assets in Latte Templates

Nette Assets provides intuitive Latte integration with tags and functions.

{asset}

The {asset} tag renders complete HTML elements:

{* Renders: <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}

{* Renders: <script src="/assets/app.js?v=456" type="module"></script> *}
{asset 'app.js'}

{* Renders: <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}

The tag automatically:

  • Detects asset type and generates appropriate HTML
  • Includes versioning for cache busting
  • Adds dimensions for images
  • Sets correct attributes (type, media, etc.)

When used inside HTML attributes, it outputs just the URL:

<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">

n:asset

For full control over HTML attributes:

{* The n:asset attribute fills in src, dimensions, etc. *}
<img n:asset="product.jpg" alt="Product" class="rounded">

{* Works with any relevant element *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>

Use variables and mappers:

{* Variables work naturally *}
<img n:asset="$product->image">

{* Specify mapper with curly brackets *}
<img n:asset="images:{$product->image}">

{* Specify mapper with array notation *}
<img n:asset="[images, $product->image]">

asset()

For maximum flexibility, use the asset() function:

{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>

{* Or directly *}
<img src={asset('logo.png')} alt="Logo">

Optional Assets

Handle missing assets gracefully with {asset?}, n:asset? and tryAsset():

{* Optional tag - renders nothing if asset missing *}
{asset? 'optional-banner.jpg'}

{* Optional attribute - skips if asset missing *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">

{* With fallback *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">

{preload}

Improve page load performance:

{* In your <head> section *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}

Generates appropriate preload links:

<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2" as="font" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg" as="image">

Advanced Features

Extension Auto-Detection

Handle multiple formats automatically:

assets:
	mapping:
		images:
			path: img
			extension: [webp, jpg, png]  # Try in order

Now you can request without extension:

{* Finds logo.webp, logo.jpg, or logo.png automatically *}
{asset 'images:logo'}

Perfect for progressive enhancement with modern formats.

Smart Versioning

Files are automatically versioned based on modification time:

{asset 'style.css'}
{* Output: <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}

When you update the file, the timestamp changes, forcing browser cache refresh.

Control versioning per asset:

// Disable versioning for specific asset
$asset = $assets->getAsset('style.css', ['version' => false]);

// In Latte
{asset 'style.css', version: false}

Font Assets

Fonts get special treatment with proper CORS:

{* Proper preload with crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}

{* Use in CSS *}
<style>
@font-face {
	font-family: 'Open Sans';
	src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
	font-display: swap;
}
</style>

Custom Mappers

Create custom mappers for special needs like cloud storage or dynamic generation:

use Nette\Assets\Mapper;
use Nette\Assets\Asset;
use Nette\Assets\Helpers;

class CloudStorageMapper implements Mapper
{
	public function __construct(
		private CloudClient $client,
		private string $bucket,
	) {}

	public function getAsset(string $reference, array $options = []): Asset
	{
		if (!$this->client->exists($this->bucket, $reference)) {
			throw new Nette\Assets\AssetNotFoundException("Asset '$reference' not found");
		}

		$url = $this->client->getPublicUrl($this->bucket, $reference);
		return Helpers::createAssetFromUrl($url);
	}
}

Register in configuration:

assets:
	mapping:
		cloud: CloudStorageMapper(@cloudClient, 'my-bucket')

Use like any other mapper:

{asset 'cloud:user-uploads/photo.jpg'}

The Helpers::createAssetFromUrl() method automatically creates the correct asset type based on file extension.

version: 1.0