← DEVLOG
Astronomy2025.10.125 min read

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

nasa-apirss-feedsession-cachegallerylightbox

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.

Content related to this post

Try it yourself