Static Assets
Catmint serves static files from the app/public/ directory. Files placed in this directory are served at the root URL without any processing, transformation, or hashing. This guide covers the public directory convention, how files are served, cache behavior, and recommended usage patterns.
The app/public/ Directory
The app/public/ directory is the designated location for static assets. Any file placed here is served directly at the corresponding root URL path:
app/
public/
favicon.ico -> /favicon.ico
robots.txt -> /robots.txt
manifest.json -> /manifest.json
og-image.png -> /og-image.png
images/
logo.svg -> /images/logo.svg
hero.jpg -> /images/hero.jpg
fonts/
inter.woff2 -> /fonts/inter.woff2
The directory structure inside public/ is preserved in the URL path. Subdirectories become path segments.
No Processing or Transformation
Files in app/public/ are served exactly as they are. Unlike assets imported in components (which go through Vite's build pipeline), public files receive:
- No bundling -- files are not processed by Vite
- No hashing -- filenames are not modified with content hashes
- No minification -- content is served as-is
- No transformation -- images are not optimized, CSS is not compiled
This makes public/ ideal for files that must be served at a fixed, predictable URL -- such as favicon.ico or robots.txt -- and files that are already optimized.
Referencing Static Assets
Reference public files using absolute URLs from the root. Since the files are served at the root path, use a leading slash:
// app/layout.tsx
import React from 'react'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json" />
<meta property="og:image" content="/og-image.png" />
</head>
<body>
<header>
<img src="/images/logo.svg" alt="Logo" width={120} height={40} />
</header>
<main>{children}</main>
</body>
</html>
)
}
Cache Headers in Production
In production, Catmint serves static assets with appropriate cache headers. Since public files do not have content hashes in their filenames, they use conservative cache settings by default:
| Environment | Cache-Control |
|---|---|
| Development | no-cache |
| Production | public, max-age=3600, immutable (for hashed assets via Vite); public, max-age=300 (for public/ files) |
Because public files can change between deployments without a filename change, they receive a shorter max-age than Vite-processed assets (which have content hashes and can be cached indefinitely). You can adjust cache behavior by configuring your deployment adapter or CDN.
Recommended Files for app/public/
Place the following types of files in the public directory:
| File | Purpose |
|---|---|
favicon.ico | Browser tab icon |
robots.txt | Search engine crawling directives |
manifest.json | PWA web app manifest |
sitemap.xml | Search engine sitemap |
og-image.png | Open Graph social sharing image |
.well-known/* | Verification files, security.txt, etc. |
Public Files vs. Imported Assets
Catmint supports two ways to include assets. Choose the right approach based on your needs:
| Feature | app/public/ Files | Imported Assets |
|---|---|---|
| URL | Fixed, predictable path | Hashed filename |
| Caching | Conservative (shorter TTL) | Aggressive (immutable, long TTL) |
| Processing | None | Optimized by Vite |
| Best for | favicon, robots.txt, manifest | Images in components, CSS |
Importing Assets in Components
For assets that are referenced in components and benefit from Vite's processing pipeline, import them directly instead of placing them in public/:
// app/components/Hero.tsx
import heroImage from './hero.jpg'
export function Hero() {
// heroImage is a hashed URL like /assets/hero-a1b2c3d4.jpg
return (
<section>
<img src={heroImage} alt="Hero banner" />
</section>
)
}
Imported assets go through Vite's build pipeline, receiving content hashing, optimization, and aggressive cache headers. Use this approach for any asset that is referenced in your component code.
Example: robots.txt and manifest.json
# app/public/robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
// app/public/manifest.json
{
"name": "My Catmint App",
"short_name": "Catmint App",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Next Steps
- Routing -- file-based routing and URL patterns
- React Server Components -- import assets in server and client components
- Caching -- cache headers and CDN configuration
