Skip to content

Asset Management

Asset management in Astro involves understanding where to place files and how they’re processed. The FiNAN website uses two directories for assets, each with different purposes.

finan-website/
├── src/assets/ # Processed by Astro (optimized)
│ └── images/
│ └── committee/ # Committee member photos
└── public/ # Served as-is (not processed)
├── images/
├── favicon.svg
└── robots.txt

Use src/assets/ for images that need optimization.

  • ✅ Committee member photos
  • ✅ Hero background images
  • ✅ Partner logos
  • ✅ Any image referenced in Astro components
  • ✅ Any image that needs responsive optimization

When you import an image from src/assets/, Astro:

  1. Optimizes - Converts to WebP/AVIF for modern browsers
  2. Resizes - Generates multiple sizes for responsive images
  3. Fingerprints - Adds content hash to filename for caching
  4. Includes metadata - Width, height, format information
src/components/Committee.astro
---
import type { CommitteeMember } from '@/data/representation/committee/types';
interface Props {
members: CommitteeMember[];
}
const { members } = Astro.props;
---
<div class="members-grid">
{members.map(member => (
<div class="member-card">
<!-- Image is optimized automatically -->
<img
src={member.photo.src}
alt={member.name}
width={member.photo.width}
height={member.photo.height}
/>
<h3>{member.name}</h3>
<p>{member.position}</p>
</div>
))}
</div>

When you import an image, you get an ImageMetadata object:

import myPhoto from '@/assets/images/committee/finland/john-doe.jpg';
console.log(myPhoto);
// {
// src: "/_astro/john-doe.a1b2c3d4.jpg",
// width: 800,
// height: 1000,
// format: "jpg"
// }

This metadata is used for:

  • Setting proper width and height attributes (prevents layout shift)
  • Generating responsive srcset for different screen sizes
  • Type checking in TypeScript

Use public/ for assets that should be served as-is without processing.

  • favicon.svg or favicon.ico
  • robots.txt
  • sitemap.xml
  • ✅ Social media images (og-image.jpg)
  • ✅ Any file that needs a specific, unchanging URL
  • ✅ Third-party assets (e.g., files from external sources)

Files in public/ are:

  1. Copied as-is - No optimization or processing
  2. Served from root - /public/image.jpg becomes /image.jpg
  3. Referenced by path - Use /image.jpg not @/public/image.jpg
src/pages/index.astro
---
const seo = {
title: 'FiNAN',
description: 'Filipino Nurses Association in the Nordic Region',
ogImage: '/images/og-image.jpg', // Served from public/images/og-image.jpg
};
---
<head>
<meta property="og:image" content={seo.ogImage} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>

If you’re a content editor adding images, use this decision tree:

Is this a committee member photo?
└─ YES → src/assets/images/committee/[country]/
Is this a logo or partner image?
└─ YES → src/assets/images/partners/ or logos/
Is this a hero background or banner?
└─ YES → src/assets/images/heroes/
Is this an Open Graph image for social media?
└─ YES → public/images/
Is this a favicon or robots.txt?
└─ YES → public/

Most important for content editors!

src/assets/images/committee/
├── finland/
│ ├── john-doe.jpg
│ ├── jane-smith.jpg
│ └── mike-johnson.jpg
├── sweden/
│ ├── anna-svensson.jpg
│ └── lars-andersson.jpg
├── norway/
│ └── ...
└── [other countries]/
  • Use kebab-case (lowercase with hyphens)
  • No spaces in filenames
  • Match member name for easy reference
john-doe.jpg
jane-smith.jpg
maria-garcia.jpg
anna-svensson.jpg
  • Format: JPEG (.jpg) preferred for photos
  • Size: Minimum 400x500px, recommended 800x1000px
  • Aspect Ratio: Portrait orientation (4:5 ratio works well)
  • File Size: Under 500KB (Astro will optimize further)
  • Quality: High quality, well-lit, professional appearance
  1. Prepare the photo

    • Save as JPEG format (.jpg)
    • Use kebab-case filename: john-doe.jpg
    • Ensure good quality and lighting
  2. Upload to correct country folder

    Navigate to src/assets/images/committee/[country]/ and add your photo.

    Example for Finland: src/assets/images/committee/finland/john-doe.jpg

  3. Import in committee data file

    Open the committee file (e.g., src/data/representation/committee/finlandCommittee.ts):

    // Add at the top with other imports
    import photoJohnDoe from '@/assets/images/committee/finland/john-doe.jpg';
  4. Add member to array

    export const finlandCommittee: CommitteeData = {
    members: [
    {
    name: 'John Doe',
    position: 'President',
    photo: photoJohnDoe, // Use the imported variable
    country: 'Finland',
    },
    // ... other members
    ],
    // ...
    };
  5. Verify the build

    Run pnpm build to ensure no errors. If successful, your image is properly configured!

Wrong:

{
name: 'John Doe',
photo: '/images/committee/finland/john-doe.jpg', // String path
}

Correct:

import photoJohnDoe from '@/assets/images/committee/finland/john-doe.jpg';
{
name: 'John Doe',
photo: photoJohnDoe, // Imported ImageMetadata object
}

Wrong:

import photoJohnDoe from '@/assets/committee/finland/john-doe.jpg';
// Missing "images" in path

Correct:

import photoJohnDoe from '@/assets/images/committee/finland/john-doe.jpg';

Confusing:

import johnDoe from '@/assets/images/committee/finland/john-doe.jpg';

Clear:

import photoJohnDoe from '@/assets/images/committee/finland/john-doe.jpg';

Convention: Use photo prefix for all committee member photo imports.

Wrong:

import photoJohnDoe from '@/assets/images/committee/finland/john-doe.png';
// File is actually .jpg

Correct:

import photoJohnDoe from '@/assets/images/committee/finland/john-doe.jpg';

Image Optimization Details (For Developers)

Section titled “Image Optimization Details (For Developers)”

When you build the site:

  1. Format Conversion

    • Generates WebP and AVIF versions for modern browsers
    • Falls back to original format for older browsers
  2. Responsive Sizes

    • Creates multiple sizes: 400w, 800w, 1200w, 1600w
    • Browser downloads appropriate size based on screen
  3. Content Hashing

    • Adds hash to filename: john-doe.a1b2c3d4.jpg
    • Enables long-term caching without stale content
  4. Metadata Extraction

    • Embeds width and height for layout stability
    • Prevents Cumulative Layout Shift (CLS)
  • Smaller File Sizes - WebP/AVIF are 25-50% smaller than JPEG
  • Faster Loading - Responsive images load appropriate sizes
  • Better SEO - Google rewards fast, stable page layouts
  • Improved Core Web Vitals - LCP and CLS improvements

For more control, use Astro’s <Image> component:

---
import { Image } from 'astro:assets';
import heroImage from '@/assets/images/heroes/main-hero.jpg';
---
<Image
src={heroImage}
alt="FiNAN Hero"
width={1920}
height={1080}
format="webp"
quality={80}
loading="lazy"
/>

Benefits:

  • Fine-tuned quality and format control
  • Lazy loading support
  • Automatic srcset generation

Error:

Cannot find module '@/assets/images/committee/finland/john-doe.jpg'

Solutions:

  1. Check file exists at the exact path
  2. Verify filename spelling and extension
  3. Ensure using @/assets/ not @/public/

Problem: Image shows broken link icon

Solutions:

  1. Verify import is correct in data file
  2. Check that photo property uses imported variable, not string
  3. Run pnpm build to see if there are type errors

Error:

Type 'string' is not assignable to type 'ImageMetadata'

Solution: You’re using a string path instead of imported image:

// ❌ Wrong
photo: '/path/to/image.jpg'
// ✅ Correct
import photoName from '@/assets/images/committee/country/name.jpg';
photo: photoName

Committee Management

Complete step-by-step guide for managing committee members

View Guide →

Data Configuration

Learn how committee data is structured and typed

View Guide →

Creating Components

Learn how components use optimized images

View Guide →