Skip to content

Committee

The Committee component displays committee member profiles in a responsive grid layout, showing photos, names, roles, bios, and LinkedIn links.

  • Type: Content Component
  • Category: Team Display
  • Complexity: Moderate
  • File: src/components/Committee.astro
  • Type-Safe Data - Uses TypeScript interfaces from committee types
  • Configuration-Driven - Members defined in country-specific config files
  • Image Handling - Supports both imported and string-path images
  • Fallback Content - Placeholder shown when no members exist
  • Responsive Grid - Adapts to screen size
  • Social Links - LinkedIn integration for networking
interface CommitteeProps extends Partial<CommitteeConfig> {
country?: CommitteeCountry;
heading?: string;
description?: string;
}
PropTypeDefaultDescription
countryCommitteeCountryundefinedCountry code to load committee config
membersCommitteeMember[]From config or fallbackArray of committee members
headingstring'Committee'Section heading text
descriptionstringundefinedOptional description below heading
---
import Committee from '@/components/Committee.astro';
---
<!-- Automatically loads finlandCommittee from config -->
<Committee country="finland" />
interface CommitteeMember {
name: string;
role: string;
bio: string;
imageAlt: string;
imageSrc?: string | ImageMetadata; // Optional image
linkedinUrl?: string; // Optional LinkedIn
affiliations?: string; // Optional org affiliations
}

Committee members are defined in country-specific files:

src/data/representation/committee/
├── finlandCommittee.ts Best practice example
├── swedenCommittee.ts
├── norwayCommittee.ts
├── icelandCommittee.ts
├── kingdomDenmarkCommittee.ts
└── types.ts
src/data/representation/committee/finlandCommittee.ts
import type { CommitteeMember } from './types';
import johnDoeImage from '@/assets/images/committee/finland/john-doe.webp';
export const finlandCommittee: CommitteeMember[] = [
{
name: 'John Doe',
role: 'President',
bio: 'John has been serving Finnish healthcare since 2015...',
imageSrc: johnDoeImage,
imageAlt: 'John Doe - President of FiNAN Finland',
linkedinUrl: 'https://linkedin.com/in/johndoe',
affiliations: 'Helsinki University Hospital',
},
];

The component supports two image formats:

import memberPhoto from '@/assets/images/committee/finland/jane-doe.webp';
{
imageSrc: memberPhoto, // ImageMetadata type
imageAlt: 'Jane Doe portrait',
}

Benefits:

  • Automatic optimization
  • Type safety
  • Build-time validation
{
imageSrc: '/images/john-doe.jpg', // String path
imageAlt: 'John Doe portrait',
}

Use when:

  • Images are in public/ directory
  • External image URLs (not recommended)

If imageSrc is not provided, a placeholder icon appears:

<div class="flex h-full w-full items-center justify-center bg-gray-200">
<svg class="h-8 w-8 text-gray-400" viewBox="0 0 48 48">
<!-- Placeholder image icon -->
</svg>
</div>
  • Grid: Flexbox with wrapping
  • Card Style: Member info beside circular photo
  • Responsive: Stacks gracefully on mobile
  • Size: 96x96 pixels (24 Tailwind units)
  • Shape: Circular (rounded-full)
  • Border: Light gray border
  • Object Fit: Cover for proper cropping
  • Name: xl, bold, gray-900
  • Affiliations: sm, gray-900 (if present)
  • Role: lg, gray-900
  • Bio: sm, gray-700, relaxed leading
  • White background with shadow
  • Blue hover state
  • LinkedIn icon from assets
  1. Prepare member photo

    • Format: WebP recommended
    • Size: At least 200x200px
    • Name: kebab-case (e.g., john-doe.webp)
    • Location: src/assets/images/committee/[country]/
  2. Import photo in config file

    import johnDoe from '@/assets/images/committee/finland/john-doe.webp';
  3. Add member object to array

    export const finlandCommittee: CommitteeMember[] = [
    // ... existing members
    {
    name: 'John Doe',
    role: 'Secretary',
    bio: 'Brief bio about John...',
    imageSrc: johnDoe,
    imageAlt: 'John Doe - Secretary',
    linkedinUrl: 'https://linkedin.com/in/johndoe',
    },
    ];
  4. Build and test

    Terminal window
    pnpm build

Cause: Incorrect image import path

Solution: Verify file exists and path matches:

import photo from '@/assets/images/committee/finland/john-doe.webp';
// ^^ Alias ^^ Country ^^ Exact filename

Cause: Missing required fields

Solution: Ensure all required fields are present:

  • name, role, bio, imageAlt are required
  • imageSrc, linkedinUrl, affiliations are optional

Cause: Image file doesn’t exist

Solution: Ensure image file exists at the specified path before importing.

  • Alt Text: Every image requires descriptive imageAlt
  • Semantic HTML: Uses <article> for each member
  • Link Labels: LinkedIn links have clear purpose
  • Heading Structure: Proper <h2> for section, <h3> for names
  • Lazy Loading: Images use loading="lazy" except above fold
  • Optimized Images: Astro optimizes WebP images automatically
  • Responsive Images: Correct size served per device

Do:

  • Use WebP format for photos
  • Write descriptive bios (2-3 sentences)
  • Include LinkedIn URLs for networking
  • Use consistent photo dimensions

Don’t:

  • Use high-resolution photos (wastes bandwidth)
  • Leave alt text generic (“image” or “photo”)
  • Hardcode member data in the component
  • Mix imported and string-path images in same config
  • Committee Types (src/data/representation/committee/types.ts)
  • Committee Configs (src/data/representation/committee/[country]Committee.ts)
  • Astro Image (astro:assets)
  • All 8 representation pages:
    • /representation/finland
    • /representation/sweden
    • /representation/norway
    • /representation/iceland
    • /representation/kingdom-denmark

View the complete implementation:

https://github.com/poncardasm/finan-website/blob/main/src/components/Committee.astro
https://github.com/poncardasm/finan-website/blob/main/src/data/representation/committee/types.ts
https://github.com/poncardasm/finan-website/blob/main/src/data/representation/committee/finlandCommittee.ts