Mars Rover — Real Photos Through the Eyes of Mars
A gallery for browsing actual photos taken by NASA Mars rovers — RSS feed parsing, session caching, and camera filters
The Goal — Eyes on Mars
Curiosity and Perseverance transmit hundreds of photos back to Earth every day. NASA publishes these images through a public RSS feed.
This page parses that feed so you can select a Sol (Martian day) and camera, then browse the actual photographs the rover captured. No composites, no renders — real Martian surface imagery.
NASA RSS Feed API
NASA's mars.nasa.gov provides a JSON-based RSS feed. Query by rover name, Sol number, and page count to receive a list of matching images.
// URL builder — mission code identifies the rover
const MISSION_MAP: Record<RoverName, string> = {
curiosity: 'msl',
perseverance: 'mars2020',
};
function buildUrl(rover: RoverName, sol: number): string {
const mission = MISSION_MAP[rover];
return `${RSS_BASE}?feed=raw_images&category=${mission}&feedtype=json&num=100&page=0&sol=${sol}`;
}
In development, requests go through a Next.js rewrite proxy (/api/mars-rss). In production, they hit NASA's servers directly. This structure sidesteps CORS restrictions.
Data Normalization
NASA's image objects have nested structures like camera.instrument and image_files.small/medium/large/full_res. We flatten these into a simpler shape for the UI.
function mapPhoto(img: MarsRssImage, idx: number): MarsPhoto {
return {
id: img.imageid ?? `${img.sol}_${idx}`,
sol: img.sol,
cameraName: img.camera.instrument,
cameraFullName: CAMERA_NAMES[img.camera.instrument] ?? img.camera.instrument,
imgSrc: img.image_files.medium || img.image_files.small,
fullResSrc: img.image_files.full_res || img.image_files.large,
earthDate: img.date_taken_utc?.split('T')[0] ?? '',
credit: img.credit,
};
}
Camera codes like FHAZ_RIGHT_A and MCZ_LEFT are mapped to human-readable names (Front Hazard Cam Right, Mastcam-Z Left). Curiosity and Perseverance use different naming schemes, so both sets are maintained.
Session Cache
To avoid repeated network requests when revisiting the same Sol, results are cached in sessionStorage.
const CACHE_PREFIX = 'mars_cache_';
function cacheKey(rover: RoverName, sol: number, camera?: string): string {
return camera ? `${rover}_${sol}_${camera}` : `${rover}_${sol}`;
}
sessionStorage clears when the tab closes, so there's no persistent storage overhead. An SSR guard (typeof sessionStorage === 'undefined') prevents server-side errors during rendering.
Camera Filters
A single Sol contains photos from multiple cameras. The uniqueCameras() function extracts which cameras are present, rendering them as filter chips.
When a user selects a specific camera, the gallery filters to show only that camera's images. This makes it easy to compare different perspectives of the same location — the same rock formation seen through different lenses.
Lightbox
Clicking a photo card opens the full-resolution image in an overlay. backdrop-filter: blur(8px) softens the background, while camera name and date appear at the bottom.
A link to open the original in a new tab is also provided. NASA images are public domain and free to use.
Closing Thoughts
Mars is no longer a subject of pure imagination. New photos arrive on Earth every day. This page aims to be the quickest window to those images.
Changing the Sol number and watching Mars shift through seasons, viewing the same spot through different cameras — small interactions that let you become part of the exploration.