Instatic Admin Router vs react-router-dom: Architecture and Performance Differences
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 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 include:
useAdminNavigate: Returns a navigate function scoped to admin routesuseAdminLocation: Provides access to the current location objectuseAdminMatch: Returns match data for the current route patternuseAdminParams: 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.
// 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. 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:
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:
// 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:
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.tsenforce the ban on react-router-dom imports within admin code - The API surface mirrors react-router-dom through scoped hooks like
useAdminNavigateanduseAdminLocation - 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 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →