Chapter : Modern Asset Import Patterns
Modern Asset Import Patterns
2025 Asset Management: Modern React applications use sophisticated build tools (Vite, Next.js, Turbopack) that provide advanced asset optimization, automatic format conversion, and intelligent caching strategies.
Overview
Asset importing has evolved significantly from basic Webpack file-loader patterns. Modern build tools provide:
- Automatic optimization: Images are compressed and converted to optimal formats
- Intelligent caching: Assets are hashed for long-term browser caching
- Format conversion: Automatic WebP/AVIF generation with fallbacks
- Size variants: Multiple resolution versions for responsive images
Static Asset Imports
Modern build tools automatically optimize and process static assets during the build process:
Basic Asset Imports
// Modern asset imports with automatic optimization
import heroImage from './assets/hero.jpg';
import logoSvg from './assets/logo.svg';
import iconPng from './assets/icon.png';
import dataJson from './assets/data.json';
function HomePage() {
return (
<div>
<img
src={heroImage}
alt="Hero banner"
loading="lazy" // Modern browsers support native lazy loading
/>
<img src={logoSvg} alt="Company logo" />
</div>
);
}
Asset Import with Size Variants
// Import multiple sizes (Vite/Next.js automatically generates variants)
import heroSmall from './assets/hero.jpg?w=400';
import heroMedium from './assets/hero.jpg?w=800';
import heroLarge from './assets/hero.jpg?w=1200';
function ResponsiveImage() {
return (
<img
src={heroLarge}
srcSet={`
${heroSmall} 400w,
${heroMedium} 800w,
${heroLarge} 1200w
`}
sizes="(max-width: 400px) 400px, (max-width: 800px) 800px, 1200px"
alt="Responsive hero image"
loading="lazy"
/>
);
}
Dynamic Asset Loading
Modern patterns for loading assets based on runtime conditions:
Dynamic Imports with Suspense
import React, { Suspense, lazy } from 'react';
// Dynamically import heavy components with their assets
const ImageGallery = lazy(() => import('./components/ImageGallery'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
function MediaSection({ mediaType }) {
return (
<Suspense fallback={<div>Loading media...</div>}>
{mediaType === 'images' && <ImageGallery />}
{mediaType === 'video' && <VideoPlayer />}
</Suspense>
);
}
Conditional Asset Loading
import { useState, useEffect } from 'react';
function AdaptiveImage({ imageName, fallback }) {
const [imageSrc, setImageSrc] = useState(fallback);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadImage = async () => {
try {
// Dynamic import based on runtime conditions
const { default: image } = await import(`./assets/images/${imageName}.jpg`);
setImageSrc(image);
} catch (error) {
console.warn(`Failed to load ${imageName}, using fallback`);
setImageSrc(fallback);
} finally {
setLoading(false);
}
};
loadImage();
}, [imageName, fallback]);
if (loading) {
return <div className="image-placeholder">Loading...</div>;
}
return <img src={imageSrc} alt={imageName} loading="lazy" />;
}
TypeScript Asset Types
2025 TypeScript Best Practice: Define proper types for all assets to ensure type safety:
Asset Type Declarations
// types/assets.d.ts
declare module '*.jpg' {
const src: string;
export default src;
}
declare module '*.jpeg' {
const src: string;
export default src;
}
declare module '*.png' {
const src: string;
export default src;
}
declare module '*.svg' {
const src: string;
export default src;
}
declare module '*.webp' {
const src: string;
export default src;
}
declare module '*.avif' {
const src: string;
export default src;
}
declare module '*.mp4' {
const src: string;
export default src;
}
declare module '*.webm' {
const src: string;
export default src;
}
// For query parameters (Vite)
declare module '*?url' {
const src: string;
export default src;
}
declare module '*?raw' {
const src: string;
export default src;
}
declare module '*?inline' {
const src: string;
export default src;
}
Typed Asset Components
// components/Image.tsx
interface ImageProps {
src: string;
alt: string;
width?: number;
height?: number;
loading?: 'lazy' | 'eager';
className?: string;
}
export function Image({
src,
alt,
width,
height,
loading = 'lazy',
className
}: ImageProps) {
return (
<img
src={src}
alt={alt}
width={width}
height={height}
loading={loading}
className={className}
/>
);
}
// Usage with imported assets
import heroImage from './assets/hero.jpg';
function Hero() {
return (
<Image
src={heroImage}
alt="Hero banner"
width={1200}
height={600}
className="hero-image"
/>
);
}
Best Practices
Asset Organization
src/
├── assets/
│ ├── images/
│ │ ├── heroes/
│ │ ├── icons/
│ │ └── gallery/
│ ├── videos/
│ └── fonts/
├── components/
└── types/
└── assets.d.ts
Performance Considerations
- Use appropriate formats: AVIF > WebP > JPEG for photos, SVG for icons
- Size variants: Generate multiple sizes for responsive images
- Lazy loading: Use
loading="lazy"for below-the-fold images - Preload critical assets: Use
<link rel="preload">for above-the-fold images
Common Pitfalls
- Don’t import large assets synchronously - Use dynamic imports for heavy media
- Avoid inline images in components - Import assets at module level
- Don’t skip alt text - Always provide meaningful alt attributes
- Don’t ignore TypeScript types - Proper typing prevents runtime errors
Navigation
- Previous: Chapter 12 Overview
- Next: Build Tool Asset Handling