5.5 KiB
Asset Handling
How to place static assets (images, icons, fonts, PDFs, stylesheets) inside an FSD project. Assets follow the same placement rules as code: group by use case, not by type, and keep them next to the code that uses them.
Caution: A custom top-level
assetssegment that aggregates all static files is not recommended. It violates the FSD principles of high cohesion and locality of changes. Place assets where they are used.
Decision Tree
- Used by exactly one slice? Keep the asset inside that slice, usually
in the
ui/segment, or inmodel/if it is part of business logic. - Reused across the app (icons, placeholder images)? Move to
shared/ui/. - Global stylesheet, font, or app-level resource? Place in the
app/layer (app/styles/,app/fonts/). - Served as-is by the bundler (favicon, robots.txt)? Use the framework's
public/folder. Thepublic/folder is not part of FSD and does not conflict with FSD layers.
Slice-specific Assets
When an asset belongs to one page, widget, or feature, keep it inside that slice. The asset lives next to the component that renders it:
pages/
home/
ui/
hero-image.jpg ← Used only by HomePage
HomePage.tsx
index.ts
If a slice uses many static images, group them in a subfolder of ui/:
pages/
home/
ui/
previews/
cake.jpg
pizza.jpg
sushi.jpg
HomePage.tsx
index.ts
Non-UI Assets
Some assets are not part of the UI but are coupled to business logic. For
example, a PDF template used to generate invoices. Place these in the
model/ segment alongside the logic that consumes them, not in ui/:
features/
billing/
model/
invoice-template.pdf ← Coupled to create-invoice.ts
create-invoice.ts
index.ts
The principle is locality of changes: if you delete the slice, every file it owns goes with it. An asset that lives in business logic should sit next to that logic.
Shared Assets
When the same asset appears across multiple slices, move it to shared/ui/.
Place reusable images in a topical subfolder, or place a single asset next to
the shared component that uses it:
shared/
ui/
placeholders/ ← Reused placeholder images
cake.jpg
pizza.jpg
Dropdown.tsx
chevron.svg ← Used only by Dropdown, kept next to it
A single icon used by exactly one component in the UI kit stays next to that component. A library of icons or images reused across many components goes in a topical subfolder.
Global Assets
Global stylesheets and fonts belong in the app/ layer because they are
imported by the application entrypoint, not by individual slices:
app/
styles/
reset.css
global.css
fonts/
inter.woff2
main.ts
Theme variables, CSS resets, and font registrations are app-wide concerns. They bootstrap the application's visual layer the same way providers bootstrap the runtime layer.
Public Folder
Most bundlers expose a public/ folder at the project root. Files here are
served as-is, without bundling or hashing.
- Vite, Next.js, Nuxt:
public/at the project root. - Astro:
public/at the project root (path is fixed and cannot be changed).
public/ is not part of FSD. It does not collide with FSD layers and does
not need to live under src/. Use it for files that must be served at fixed
URLs: favicon, robots.txt, sitemap.xml, OG images, and similar.
public/
favicon.ico
robots.txt
og-image.png
src/
app/
pages/
shared/
Some projects keep a project-local app/public/ folder when the bundler
allows assets to live alongside the entrypoint. Both layouts are valid.
Summary Table
| Asset | Location |
|---|---|
| Image used by one page/widget/feature | Inside the slice's ui/ segment |
| PDF or template tied to business logic | Inside the slice's model/ segment |
| Icon reused across the app | shared/ui/ (topical subfolder if many) |
| Icon used by exactly one shared kit UI | Next to that component in shared/ui/ |
| Global CSS reset, theme variables | app/styles/ |
| Web fonts | app/fonts/, public/, or app/public/ |
| Favicon, robots.txt, sitemap | public/ (or app/public/) |
Anti-patterns
- Do not create a top-level
assets/segment that holds all images, fonts, and icons. It breaks cohesion and forces consumers to import from a folder unrelated to the code they are working on. - Do not extract a slice-local asset to
shared/"in case" it gets reused. Move it only when actual reuse appears. - Do not place CSS modules in an
assets/folder. A component's stylesheet belongs next to that component inui/. - Do not name an FSD segment
public. The framework'spublic/folder is reserved and lives outsidesrc/. - Do not split assets and the components that use them. A page that ships a hero image should keep that image in the page so removing the page removes the image.
See Also
references/layer-structure.md: segment rules and layer organization- Desegmentation: why
technical-role grouping (including a generic
assets/segment) hurts cohesion