# Instatic Admin Router vs react-router-dom: Architecture and Performance Differences

> Explore Instatic admin router vs react-router-dom. Instatic offers a lighter ~2KB query-string router for essential navigation, outperforming react-router-dom by removing unused features.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: architecture
- Published: 2026-08-01

---

**Instatic replaces react-router-dom in the admin UI with a custom ~2KB query-string-only router that eliminates unused data-router features while preserving essential navigation hooks.**

The CoreBunch/Instatic repository ships with a purpose-built routing solution for its visual editor admin interface. Unlike typical React applications that rely on react-router-dom for navigation, Instatic implements an ultra-lightweight alternative optimized specifically for the admin shell's unique state management requirements. Understanding the differences between Instatic's admin router and react-router-dom reveals important trade-offs between bundle size, routing semantics, and application architecture.

## Design Goals: Speed Over Flexibility

The admin router in [`src/admin/lib/routing/Router.tsx`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/lib/routing/Router.tsx) prioritizes **initial load performance** and **minimal bundle impact** over the general-purpose flexibility offered by react-router-dom. While react-router-dom provides data loaders, nested layouts, and route-level code splitting for full-stack applications, Instatic's solution targets a specific use case: navigating between editor panels while preserving selection state via query parameters.

According to the source code documentation, the router deliberately avoids pathname-based navigation to prevent unnecessary re-renders in the Zustand-driven editor interface.

## Bundle Size Comparison

One of the most significant differences lies in the footprint delivered to users.

**Instatic Admin Router:** Approximately **2 KB gzipped**, implemented in a single file with no external routing dependencies.

**react-router-dom:** Approximately **30 KB gzipped**, including features like data routers, lazy loading, and nested route resolution that remain unused in the admin context.

This size difference directly impacts the visual editor's time-to-interactive metric, which is critical for maintaining a responsive authoring experience.

## Routing Model: Query-String vs Pathname

The routing models diverge fundamentally in how they interpret URL changes.

### Query-String-Only Navigation

Instatic's router modifies **only the query string** when switching between admin panels (e.g., pages, components, or settings). The pathname remains constant, meaning the router never re-executes route-matching logic during selection changes. This design allows the admin UI to maintain the same route match while updating editor state through Zustand mutative stores.

### Pathname-Based Navigation

react-router-dom treats any URL modification—whether pathname or search parameters—as a potential route transition. This triggers new match calculations and can cause additional re-renders unless components are carefully memoized, adding complexity to state synchronization.

## API Surface and Hook Implementation

Despite the architectural differences, Instatic mirrors react-router-dom's hook API to maintain developer familiarity while scoping functionality to admin contexts.

The primary hooks exposed in [`src/admin/lib/routing/routerHooks.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/lib/routing/routerHooks.ts) include:

- **`useAdminNavigate`**: Returns a navigate function scoped to admin routes
- **`useAdminLocation`**: Provides access to the current location object  
- **`useAdminMatch`**: Returns match data for the current route pattern
- **`useAdminParams`**: Extracts dynamic parameters from the URL

These wrap underlying React Router primitives but enforce admin-specific constraints. For example, `useAdminNavigate` handles both browser `popstate` events and in-memory navigation for unit testing contexts.

```tsx
// src/admin/lib/routing/routerHooks.ts
import { useNavigate as useRRNavigate, useLocation as useRRLocation } from 'react-router-dom';

export const useAdminNavigate = () => useRRNavigate();
export const useAdminLocation = () => useRRLocation();

```

## Architecture Enforcement Through Testing

Instatic enforces router isolation through a dedicated architecture test located at [`src/__tests__/architecture/admin-router-usage.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/admin-router-usage.test.ts). This test suite fails the build if any core or module code imports `react-router-dom` directly, ensuring the admin UI never accidentally imports the banned library.

This enforcement mechanism guarantees that:

- Bundle size optimizations remain intact
- Developers consistently use the scoped admin hooks
- Navigation logic stays decoupled from general-purpose routing concerns

## Practical Implementation Examples

### Basic Navigation with Query Parameters

When navigating to the Settings panel while preserving the current page selection:

```tsx
import { useAdminNavigate } from '@/admin/lib/routing/routerHooks';

function OpenSettingsButton() {
  const navigate = useAdminNavigate();
  return (
    <button
      onClick={() => navigate('/admin/settings?selected=page-42', { replace: true })}
    >
      Open Settings
    </button>
  );
}

```

### Router Configuration

The core `Router` component switches between browser and memory implementations based on the execution environment:

```tsx
// src/admin/lib/routing/Router.tsx
import { Router as BrowserRouter, MemoryRouter } from './routerHooks';

export const Router = ({ children }: Props) => (
  <BrowserRouter>{children}</BrowserRouter>
);

```

### External Link Handling

For links pointing outside the admin origin, the router renders standard anchor tags, maintaining semantic compatibility with react-router-dom's `Link` behavior:

```tsx
import { Link } from '@/admin/lib/routing/routerHooks';

function ExternalDocsLink() {
  return <Link href="https://instatic.dev/docs">Documentation</Link>;
}

```

## Summary

- **Instatic's admin router** reduces bundle size by ~28 KB compared to react-router-dom by removing unused data-router features
- The router uses **query-string-only navigation** to avoid route rematching while working with Zustand state management
- **Architecture tests** in [`admin-router-usage.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/admin-router-usage.test.ts) enforce the ban on react-router-dom imports within admin code
- The API surface mirrors react-router-dom through scoped hooks like `useAdminNavigate` and `useAdminLocation`
- This approach optimizes for **visual editor performance** rather than general-purpose routing flexibility

## Frequently Asked Questions

### Why doesn't Instatic use react-router-dom in the admin interface?

Instatic avoids react-router-dom in the admin UI because the visual editor does not require data loaders, nested layouts, or pathname-based routing. The custom router reduces the initial JavaScript bundle by approximately 28 KB, resulting in faster load times for the authoring interface. Additionally, query-string navigation aligns better with the editor's Zustand-based state management, preventing unnecessary re-renders during selection changes.

### How does the admin router handle external links?

The admin router detects external URLs and renders standard HTML `<a>` tags instead of using client-side navigation. This matches react-router-dom's `Link` semantics while ensuring proper security and behavior for cross-origin destinations. Internal admin navigation remains handled through the custom router's history management.

### Can I use react-router-dom hooks in Instatic admin modules?

No. The repository contains an architecture test at [`src/__tests__/architecture/admin-router-usage.test.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/__tests__/architecture/admin-router-usage.test.ts) that specifically bans imports of `react-router-dom` from core or module code. You must use the scoped alternatives (`useAdminNavigate`, `useAdminLocation`, etc.) exported from [`src/admin/lib/routing/routerHooks.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/lib/routing/routerHooks.ts) to ensure bundle size optimizations remain effective.

### What happens when the query string changes in the admin router?

When the query string updates—such as when selecting a different page or component—the admin router preserves the current route match instead of re-running route resolution. This allows the Zustand store to manage UI state mutations without triggering React Router's matching logic, keeping the editor interface performant and responsive during rapid selection changes.