---
title: 'Storefront: Theme Assets | Shopware Community Hub'
description: >-
  Learn how to manage theme assets, such as images, icons, and thumbnails, how
  they are delivered in themes and plugins, and how to use them in Twig and
  SCSS.
canonical_url: 'https://hub.shopware.com/learn/unit/storefront-theme-assets'
---

# Storefront: Theme Assets

<LearningObjectives>

- Understand how storefront assets (icons, images, thumbnails) are structured and delivered in themes and plugins (theme-compiled vs. bundle/public assets).
- Learn how to reference theme-compiled and bundle/public assets in Twig and SCSS using `asset()`, `$app-css-relative-asset-path`, and `$sw-asset-public-url`.
- Know how to use `sw_icon`, custom SVG icon namespaces, and `sw_thumbnails` to render storefront media correctly.

</LearningObjectives>

# Storefront: Theme Assets

In Shopware storefront development, many customizations rely on correctly managing **theme assets** such as images, icons, and thumbnails.

In this learning unit, you will learn where theme assets are stored, how Shopware makes them publicly available, and how to render them correctly in Twig using `asset()`, `sw_icon` and `sw_thumbnails`.

## Theme Assets: Images, Icons, and Thumbnails

Theme assets are files like images, icons, and other static resources that your theme or plugin ships with.

In the Shopware storefront, assets typically fall into three categories:

- **Static bundle assets** (images, placeholders, UI graphics).
- **Icon assets** rendered via `sw_icon`.
- **Media assets** managed by the Shopware media system and rendered via `sw_thumbnails`.

### Where Assets Live

In a theme/plugin, assets are typically placed under your storefront source folder, for example `src/Resources/app/storefront/src/assets/`.

Shopware’s storefront build will include those assets in the compiled theme output.

### Using Custom Assets in Twig Templates

In Shopware, there are two different asset pipelines. They are both valid, but they serve different use cases.

- **Theme-compiled assets (recommended for UI/theme assets)**: Shipped in your storefront source and copied to `public/theme/...` during `theme:compile`.
- **Bundle/public assets (only when you use `Resources/public`)**: Shipped as public bundle assets and copied to `public/bundles/...` via `assets:install`.

In most storefront projects, you should prefer theme-compiled assets for UI and styling. The `Resources/public` approach is mainly used for publicly exposed files or special cases.

<Callout title="Important" type="info">

Important: You do not need to create your own theme to use theme-compiled assets. A regular storefront plugin can also ship assets under `src/Resources/app/storefront/src/...` and they will be included in the active theme build when you run `theme:compile`.

</Callout>

#### File locations overview (where to put assets and where they end up)

The following file trees show the two pipelines. The placeholders like `[your_plugin]` and `[theme-uuid]` are examples.

**Theme-compiled assets (via `theme:compile` → `public/theme/...`)**

```txt
[shop_root]
 └─ custom
    └─ plugins
       └─ [your_plugin]
           │─ src
           │  │─ Resources
           │  │  └─ app
           │  │     └─ storefront
           │  │        └─ src
           │  │           └─ assets
           │  │              └─ your-image1.png // <-- Put it here
           │  │              └─ [...].png       // <-- Put it here
           │  │              └─ your-imageN.png // <-- Put it here
           │  └─ YourPlugin.php
           └─ composer.json
-----------------------------------------------------------
[shop_root]
 │─ custom/plugins
 │   │─ Plugin1
 │   │─ [...]
 │   └─ PluginN
 │─ public
 │   └─ bundles/
 │   └─ theme
 │      └─ [theme-asset-uuid]
 │          └─ asset
 │             │─ your-image1.png // <-- After theme:compile
 │             │─ [...].png       // <-- After theme:compile
 │             └─ your-imageN.png // <-- After theme:compile
 │─ var/
 │─ vendor/
 └─ composer.json
```

**Bundle/public assets (via `assets:install` -> `public/bundles/...`)**

```txt
[shop_root]
 └─ custom
    └─ plugins
       └─ [your_plugin]
           │─ src
           │  │─ Resources
           │  │  └─ public
           │  │     └─ your-image1.png // <-- Put it here
           │  │     └─ [...].png       // <-- Put it here
           │  │     └─ your-imageN.png // <-- Put it here
           │  └─ YourPlugin.php
           └─ composer.json
-----------------------------------------------------------
[shop_root]
 │─ custom/plugins
 │   │─ Plugin1
 │   │─ [...]
 │   └─ PluginN
 │─ public
 │   │─ bundles
 │   │  └─ [bundle_name]
 │   │      │─ your-image1.png // <-- After assets:install
 │   │      │─ [...].png       // <-- After assets:install
 │   │      └─ your-imageN.png // <-- After assets:install
 │   └─ theme/
 │─ var/
 │─ vendor/
 └─ composer.json
```

<Callout title="Which Command Should You Run?" type="info">

The command depends on which asset pipeline you use.

- If you changed **theme-compiled assets** under `Resources/app/storefront/src/...`, run `bin/console theme:compile`.
- If you changed **bundle/public assets** under `Resources/public/...`, run `bin/console assets:install`.
- If you changed storefront source that needs a full storefront build, for example JavaScript, SCSS, or multiple asset types together, many projects use the classic project script `bin/build-storefront.sh` as the broader helper command. Shopware CLI equivalent is: `shopware-cli project storefront-build`.

</Callout>

#### Theme-Compiled Assets: `theme.json` and `theme:compile`

This is the recommended way. If you place an asset in your theme’s storefront source folder (for example `src/Resources/app/storefront/src/assets/`), you can register that folder in your theme’s `theme.json`.

```json
// <theme root>/src/Resources/theme.json
{
  "asset": [
    "app/storefront/src/assets"
  ]
}
```

Then compile the theme:

```bash
bin/console theme:compile
```

Shopware will copy the assets into a public theme asset directory (under `public/theme/...`) together with the compiled storefront assets.

You can reference the file in Twig like this:

```twig
<img src="{{ asset('/assets/your-image.png', 'theme') }}" alt="Example">
```

In SCSS, use the CSS-relative theme asset path variable:

```scss
.your-custom-class {
  background-image: url('#{$app-css-relative-asset-path}/your-image.png');
}
```

<Callout title="What are 'asset path variables'?" type="info">

In Storefront SCSS, Shopware exposes variables that help you build correct public URLs to assets:

- `$app-css-relative-asset-path`: Points to the compiled theme asset directory (use it for theme-compiled assets).
- `$sw-asset-public-url`: Points to the public base URL (use it for bundle/public assets like `/bundles/...`).

</Callout>

#### Bundle/Public Assets: `Resources/public` and `assets:install`

This is another optional way. If you ship assets via `[Your_Plugin]/src/Resources/public/`, you can install them into the public bundle directory using:

```bash
bin/console assets:install
```

This command copies files from your plugin’s `Resources/public/` into `public/bundles/[bundle_name]/`.

<Callout title="Note" type="info">

The `asset()` helper used in Twig is provided by Symfony (`symfony/asset`), which Shopware builds on top of.

</Callout>

Twig example:

```twig
<img src="{{ asset('bundles/yourplugin/your-image.png', 'asset') }}" alt="Example">
```

In SCSS you can reference the bundle path as well (for example when you use `Resources/public` assets):

```scss
.your-custom-class {
  background-image: url("#{$sw-asset-public-url}/bundles/yourplugin/your-image.png");
}
```

## Adding Custom Icons (`sw_icon`)

Shopware provides the `sw_icon` Twig function to render icons. You can also ship your own SVG icons and render them via `sw_icon`.

The official [Shopware icon guide](https://developer.shopware.com/docs/guides/plugins/themes/add-icons.html) describes the default location for custom icons like this:

```txt
[your_plugin]
 └─ src
    └─ Resources
       └─ app
          └─ storefront
             └─ dist
                └─ assets
                   └─ icon
                      └─ default
                         └─ done-outline-24px.svg
```

The folder below `icon` is the icon **pack**. By default, Shopware looks in the `default` pack. You can also create another pack folder, for example `solid` or a custom pack name.

<Callout title="Why Is This Inside dist?" type="info">

In many storefront workflows, `dist` contains generated build output. Custom icons are a special case: the documented `dist/assets/icon/...` folder is intentionally used for icon files and can be kept in version control.

Do not confuse this with generated JavaScript or CSS build artifacts. For custom icons, follow the documented icon path unless your project uses a custom `iconSets` configuration.

</Callout>

The most important configuration when using custom icons is the **namespace**. It tells Shopware which plugin or theme should be searched for the icon.

With the default icon pack, the icon can be rendered with `sw_icon` and your namespace:

```twig
{% sw_icon 'done-outline-24px' style { 'namespace': 'YourPluginName' } %}
```

If you use a different pack folder, pass the pack name as well:

```twig
{% sw_icon 'done-outline-24px' style {
  'namespace': 'YourPluginName',
  'pack': 'solid'
} %}
```

Shopware also supports custom icon locations in `theme.json` with the `iconSets` key:

```json
{
  "iconSets": {
    "custom-icons": "app/storefront/src/assets/icon-pack/custom-icons"
  }
}
```

You can then render an icon from that custom pack:

```twig
{% sw_icon 'done-outline-24px' style { 'pack': 'custom-icons' } %}
```

This `iconSets` setup is especially relevant when you ship a theme as an app, because custom icons need a configured location.

The second argument `style` lets you control how the icon is rendered. Common options include:

- `size`: A predefined icon size (e.g., `sm`, `md`, `lg` or absolute pixel value).
- `color`: A predefined color in hex value, also as a CSS variable (e.g. `var(--sw-color-success)`).
- `class`: Additional CSS classes added to the rendered icon.
- `pack`: The icon pack to use (default: `default`).
- `namespace`: The plugin or theme namespace where Shopware should look for the icon.
- `ariaHidden`: When `true`, the icon is hidden from screen readers (useful for purely decorative icons).
- `ariaLabel`: A text label for screen readers (use it when the icon conveys meaning and has no visible text next to it).

<Callout title="SVG Expectations for sw_icon" type="info">

Custom icons should be SVG files. Keep them as simple icon SVGs:

- Do not embed PNG, JPG, or other raster images inside the SVG.
- Avoid hardcoded fill colors when the icon should be themeable.
- Prefer `currentColor` or no fixed color so the `color` option and CSS can control the icon color.
- Keep the SVG focused on the icon shape, not on layout, text, or complex artwork.

</Callout>

**Example with a custom namespace:**

```twig
{% sw_icon 'done-outline-24px' style {
  'namespace': 'YourPluginName',
  'pack': 'default',
  'size': 'lg',
  'class': 'my-icon me-1 align-middle'
} %}
```

### Under the Hood

`sw_icon` is a Twig tag implemented in Shopware core. Internally, it includes a template (`@Storefront/storefront/utilities/icon.html.twig`) which loads the SVG using Twig’s `source()` function.

By default, Shopware uses:

- `pack = "default"`
- `namespace = "Storefront"`

An icon is therefore typically resolved from a path like `@Storefront/assets/icon/default/[name].svg`

<Callout title="Important" type="warning">

Custom icons (and other custom assets) are **not automatically overridden via theme inheritance**. Don’t expect your icon file to override a core icon just by mirroring the core folder structure.

You will learn the full theme inheritance mechanism in the next learning unit. For now, keep this practical boundary in mind: Asset folders can be included in a theme setup, but custom icon files do not silently replace core icon files through path mirroring.

</Callout>

### Replacing an Existing Icon Safely

If you want to replace an icon that a Shopware template already renders, do not rely on placing a file with the same name into a mirrored core path. That is not how theme inheritance works for icons.

The documented rule is: custom icons and other custom assets are not included in theme inheritance. The practical solution is to make the replacement explicit:

1. Add your own SVG icon under your plugin or theme namespace.
2. Override the Twig block or template where the original icon is rendered, if the core template does not expose a configuration option.
3. Replace the `sw_icon` call with your own icon name and namespace.

For example:

```twig
{% sw_icon 'custom-cart-icon' style {
  'namespace': 'YourPluginName',
  'pack': 'default',
  'size': 'md',
  'color': 'primary'
} %}
```

This makes the customization explicit: the template now asks for your icon instead of hoping that a core icon file is silently replaced.

## Working With Media and Thumbnails (`sw_thumbnails`)

In addition to shipping static assets with your extension, Shopware has a built-in **media system** (e.g., product images). For uploaded media, Shopware automatically generates thumbnails.

To render responsive images without manually writing `srcset` and `sizes`, you can use the `sw_thumbnails` Twig function:

```twig
{% sw_thumbnails 'my-thumbnail' with {
    media: cover
} %}
```

This renders an image element with an appropriate `srcset`. It requires a **media entity** (not just an ID).

The first argument (`'my-thumbnail'`) is a required identifier of the Twig tag. Use a meaningful name like `'product-image-thumbnails'` or `'cms-image-thumbnails'`.

### Common Options

- **`attributes`**: Additional HTML attributes for the `<img>` tag (e.g. `class`, `alt`, `title`, `loading`).
- **`sizes`**: A breakpoint map for the `sizes` attribute (optional). You can also set `sizes.default` to force a single size. Make sure it contains the keys your theme breakpoints (e.g., `xs`, `sm`, `md`, `lg`, `xl`, `xxl`) to avoid empty entries.
- **`columns`** + **`layout`**: If you render thumbnails inside a CMS grid, Shopware can generate `sizes` automatically based on your theme breakpoints and the column count. For `layout`, core checks for `full-width` (otherwise it assumes a boxed container).
- **`load`**: Whether `src` / `srcset` are rendered directly (`true`, default) or as `data-src` / `data-srcset` (`false`) for JavaScript-driven lazy loading.
- **`loadOriginalImage`**: When `true`, adds the original media URL to the `srcset` as the largest candidate (default: `false`).

**Example: Custom attributes and explicit sizes**

```twig
{% sw_thumbnails 'product-image-thumbnails' with {
    media: cover,
    attributes: {
        class: 'img-fluid',
        alt: cover.translated.alt ?: product.translated.name,
        title: cover.translated.title ?: product.translated.name,
        loading: 'lazy'
    },
    sizes: {
        xs: '100vw',
        sm: '50vw',
        md: '33vw',
        lg: '25vw',
        default: '400px'
    }
} %}
```

<Callout title="Under the Hood" type="info">

`sw_thumbnails` is implemented in Shopware core as a Twig tag. Internally, it includes `@Storefront/storefront/utilities/thumbnail.html.twig`, which:

- Builds a `srcset` from `media.thumbnails`.
- Can auto-generate `sizes` when you provide `columns` (and no explicit `sizes`) based on your theme breakpoints (`theme_config('breakpoint.*')`) and whether `layout` is `full-width` (otherwise it assumes a boxed container).
- Sets sensible defaults for `alt` and `title` (from `media.translated`) and `loading` (defaults to `eager` unless you override it via `attributes.loading`)

This is why `sw_thumbnails` is the preferred way to render media responsively in the Shopware storefront.

</Callout>

<Callout title="The Attribute 'loading'" type="info">

Choose `loading` intentionally:

- Use `loading: 'lazy'` for images below the fold (most listings, CMS pages).
- Keep `loading: 'eager'` (default) and consider `fetchpriority: 'high'` for the primary hero image / LCP image.

</Callout>

## Summary

In this learning unit, you learned:

- How theme assets such as images, icons, and thumbnails are structured and delivered in themes and plugins.
- How to reference theme and bundle assets in Twig and SCSS using `asset()`, `$app-css-relative-asset-path`, and `$sw-asset-public-url`.
- How to use `sw_icon`, custom SVG icon namespaces, and `sw_thumbnails` to render storefront media correctly.

With this knowledge, you can integrate and deliver storefront assets in a consistent and maintainable way across your themes and plugins.
