Files
Daniil 21e936a827
dev / deploy (push) Successful in 2m15s
compute / deploy (push) Has been cancelled
chore: agentic upgrade
2026-05-17 02:11:33 +03:00

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 assets segment 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

  1. Used by exactly one slice? Keep the asset inside that slice, usually in the ui/ segment, or in model/ if it is part of business logic.
  2. Reused across the app (icons, placeholder images)? Move to shared/ui/.
  3. Global stylesheet, font, or app-level resource? Place in the app/ layer (app/styles/, app/fonts/).
  4. Served as-is by the bundler (favicon, robots.txt)? Use the framework's public/ folder. The public/ 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 in ui/.
  • Do not name an FSD segment public. The framework's public/ folder is reserved and lives outside src/.
  • 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