Angular 18 Asset Referencing in Workspace Projects
Problem Statement
In Angular workspaces created with @angular/cli@18
, developers face challenges referencing static assets (images, fonts, etc.) placed in the new public
folder. While Angular 17 used an src/assets
structure and simple <img src="assets/image.png">
references, Angular 18's workspace structure causes:
- Asset paths not resolving correctly in templates
- Files in
/public
not copied to build output (dist
) - Confusion between project root paths and workspace paths
This occurs specifically in multi-project workspaces with this structure:
workspace-name
│
└── projects/
└── application-name/
├── public/
└── src/
Recommended Solutions
Default Configuration (Cleanest Approach)
Angular 18 configures asset handling in angular.json
by default. Place assets anywhere in the project's public
folder and reference them relative to the root URL.
Folder structure:
bashprojects/ └── application-name/ └── public/ ├── img/ │ └── logo.png ├── pdf/ │ └── guide.pdf └── favicon.ico
Reference in templates (omit
public/
):html<!-- Component template --> <img src="img/logo.png" alt="Logo"> <a href="pdf/guide.pdf">Download Guide</a>
The default angular.json
configuration:
{
"architect": {
"build": {
"options": {
"assets": [
{
"glob": "**/*",
"input": "public" // Path relative to project root
}
]
}
}
}
}
Alternative Approach: Absolute Paths
For clearer path resolution, create an assets
subfolder in public
and use absolute paths:
Folder structure:
bashpublic/ └── assets/ └── img/ └── banner.jpg
Template reference:
html<img src="/assets/img/banner.jpg">
Hybrid Configuration (Legacy Support)
To maintain compatibility with Angular 17's asset structure:
Create
src/assets/
in your project:bashprojects/ └── application-name/ ├── public/ └── src/ └── assets/
Update
angular.json
:json"assets": [ { "glob": "**/*", "input": "public" }, { "glob": "**/*", "input": "src/assets", "output": "/assets/" // Optional: maps to /assets/ in dist } ]
Reference legacy assets:
html<img src="assets/legacy-image.png" alt="Legacy Asset">
CSS Font Reference
When accessing fonts from CSS files, use absolute paths with ^
prefix:
@font-face {
font-family: 'Inter';
src: url('^public/fonts/Inter.woff2') format('woff2');
}
Troubleshooting Checklist
If assets still fail to load:
- Verify folder location: Assets must be in project-specific
public/
, not workspace root - Check angular.json: Ensure
input
path is relative to project root - Restart dev server: Run
ng serve
after configuration changes - Inspect build output: Verify files appear in
dist/
afterng build
- Use output mapping: For custom distribution paths:json
{ "glob": "**/*", "input": "public", "output": "static-assets/" }
Key Concepts
- Input Paths: Relative to project root (e.g.,
projects/your-app
) - Output Paths: Relative to build output (
dist/your-app
) - Path Resolution: Always omit
public/
in references - Glob Patterns:
**/*
matches all files recursively
Migration Tip
For projects upgraded from Angular 17, use the hybrid configuration approach for a smoother transition. New projects should use the default public/
structure.
Avoid Absolute Paths in Source
Don't use /public/...
in source paths - the public
folder name never appears in the compiled application.
Asset Reference Examples
// Use with [src] binding <img [src]="iconUrl">
## Configuration Best Practices
1. **Multiple Project Workspaces**: Each app/library should have its own `public` folder
2. **Environment Variations**: Use file replacements for environment-specific assets
3. **Asset Optimization**: Utilize Angular's built-in optimization for:
- Image compression (WebP conversion)
- Font inlining
- Cache busting
4. **Security**: Always sanitize dynamic asset URLs with `DomSanitizer`
```typescript
import { DomSanitizer } from '@angular/platform-browser';
constructor(private sanitizer: DomSanitizer) {}
getSafeUrl(path: string) {
return this.sanitizer.bypassSecurityTrustResourceUrl(path);
}