Resolved

Astro content collections show full folder paths in URLs and navigation missing

Astro
Frontend
Content Collections
URL Routing
Navigation
Markdown
TypeScript
Dev Workflow

Astro static site URLs exposed internal folder structure and new content collection lacked navigation integration | Resolved via URL path extraction and navigation component updates

Working with Astro’s Content Collections feature to implement a “Resolved” technical issue logging section, where markdown files are processed into a structured portfolio with navigation, clean URLs, and date-based sorting. The implementation uses Astro’s glob-based content loading with TypeScript schema validation for frontmatter metadata.

Issue

The newly implemented Resolved section suffered from three critical problems: 1) No navigation menu entry despite the /resolved route existing, forcing users to manually type URLs; 2) Content detail pages exposed internal folder structure through URLs (e.g., /resolved/name_2025-10-26-FastMCP20/2025-10-26-1116-fastmcp20-pure-architecturev3); 3) Dev server warnings about unprocessed .astrobackup files; 4) Inconsistent date parsing between filename conventions (2025_10_27) and frontmatter dates (2025-10-27) causing potential sorting issues.

Culprit

The Nav.astro component’s textLinks array was hardcoded with only three entries (Home, Work, About), missing the new Resolved section despite the route existing in src/pages/resolved.astro.

URL Path Exposure

Astro’s Content Collections API preserves the full relative file path as the id property. Both ResolvedPreview.astro and [...slug].astro used this raw id for URL generation without path extraction:

// Before: Used full path
const url = `/resolved/${entry.id}`;

Backup File Warnings

Astro processes all files in src/pages/ unless prefixed with _. Backup files like indexraw.astrobackup lacked this prefix, triggering warnings during dev server processing.

Date Parsing Inconsistency

Some markdown filenames used underscores (2025_10_27) while frontmatter used ISO-compatible hyphens (2025-10-27), risking sorting mismatches if filename-based parsing was attempted.

Solution

Added the Resolved section to Nav.astro’s textLinks array (line 11):

const textLinks = [
  { label: 'Home', href: '/' },
  { label: 'Work', href: '/work/' },
  { label: 'About', href: '/about/' },
  { label: 'Resolved', href: '/resolved/' }  // New entry
];

URL Path Extraction

Implemented filename extraction in both preview and detail components:

// ResolvedPreview.astro & [...slug].astro
const filename = entry.id.split('/').pop()?.replace('.md', '') || entry.id;

This transforms: /resolved/name_2025-10-26-FastMCP20/2025-10-26-1116-fastmcp20-pure-architecturev3.md → /resolved/2025-10-26-1116-fastmcp20-pure-architecturev3

Backup File Handling

Executed bash command to prefix all .astrobackup files:

cd src/pages && for f in *.astrobackup; do mv "$f" "_$f"; done

Files renamed from indexraw.astrobackup → _indexraw.astrobackup

Date Sorting Standardization

Leveraged Astro’s content collections schema with z.coerce.date() to handle multiple formats:

// src/content/config.ts
schema: z.object({
  publishDate: z.coerce.date(),  // Handles 2025-11-14, 2025-11-14T21:58:29, ISO 8601
  // ... other fields
})

Sorting implemented in resolved.astro:

const issues = (await getCollection('resolved')).sort(
  (a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
);

Content Collection Configuration

Defined collection in src/content/config.ts:

collections: {
  resolved: {
    schema: ({ image }) => z.object({
      title: z.string(),
      description: z.string(),
      publishDate: z.coerce.date(),
      tags: z.array(z.string())
    }),
    // Exclude INDEX files and load recursively
    entryFilter: (entry) => !entry.id.includes('INDEX')
  }
}