@zapstra/core-router
Version:
Query-preserving router for Zapstra platform apps
219 lines (164 loc) • 5.09 kB
Markdown
# @zapstra/core-router
Query-preserving router and navigation utilities for Zapstra platform apps.
## Features
- Query parameter preservation during navigation (essential for Shopify apps)
- Built-in loading states and progress indicators
- React context for navigation state management
- TypeScript support with full type safety
- Customizable loading spinner with gradient design
- Path matching utilities for active state detection
## Installation
```bash
npm install @zapstra/core-router
```
## Usage
### Basic Setup
```typescript
import { AppRouterProvider } from '@zapstra/core-router';
export default function App() {
return (
<AppRouterProvider>
{/* Your app content */}
</AppRouterProvider>
);
}
```
### Navigation with Query Preservation
```typescript
import { useAppRouter } from '@zapstra/core-router';
function NavigationComponent() {
const { route, isNavigating } = useAppRouter();
const handleNavigate = () => {
// Automatically preserves ?host, ?shop, and other query params
route('/app/settings');
};
return (
<button
onClick={handleNavigate}
disabled={isNavigating}
>
Go to Settings
</button>
);
}
```
### Direct Navigation Utilities
```typescript
import { useNavigateWithQuery, useQueryParams, useIsPathActive } from '@zapstra/core-router';
function MyComponent() {
const navigate = useNavigateWithQuery();
const queryParams = useQueryParams();
const isActive = useIsPathActive();
// Navigate preserving query params
const goToHome = () => navigate('/app');
// Access current query parameters
const shopDomain = queryParams.shop;
const host = queryParams.host;
// Check if path is active
const isSettingsActive = isActive('/app/settings');
const isExactHome = isActive('/app', true); // exact match
return (
<div>
<button onClick={goToHome}>Home</button>
<p>Current shop: {shopDomain}</p>
<p>Settings active: {isSettingsActive}</p>
</div>
);
}
```
### Custom Loading Spinner
```typescript
import { AppRouterProvider } from '@zapstra/core-router';
export default function App() {
return (
<AppRouterProvider
showLoadingSpinner={true}
spinnerProps={{
size: 100,
strokeWidth: 6
}}
>
{/* Your app */}
</AppRouterProvider>
);
}
```
### Without Built-in Spinner
```typescript
import { AppRouterProvider, useAppRouter, GradientProgressSpinner } from '@zapstra/core-router';
function CustomLoadingWrapper({ children }) {
const { isNavigating } = useAppRouter();
return (
<>
{isNavigating && <div>Custom loading...</div>}
{children}
</>
);
}
export default function App() {
return (
<AppRouterProvider showLoadingSpinner={false}>
<CustomLoadingWrapper>
{/* Your app */}
</CustomLoadingWrapper>
</AppRouterProvider>
);
}
```
### Higher-Order Component
```typescript
import { withAppRouter } from '@zapstra/core-router';
function MyApp() {
return <div>My App Content</div>;
}
export default withAppRouter(MyApp, {
showLoadingSpinner: true,
spinnerProps: { size: 80 }
});
```
## Why Query Preservation?
Shopify apps run in embedded iframes and require specific query parameters like `?host` and `?shop` to function correctly. Standard navigation can lose these parameters, breaking the app. This router automatically preserves all query parameters during navigation.
```typescript
// Without @zapstra/core-router - BREAKS SHOPIFY APPS
navigate('/app/settings'); // Loses ?host=... and ?shop=...
// With @zapstra/core-router - WORKS CORRECTLY
const { route } = useAppRouter();
route('/app/settings'); // Preserves ?host=... and ?shop=...
```
## API Reference
### Components
#### `AppRouterProvider`
Main provider component that wraps your app.
**Props:**
- `children: ReactNode` - Your app content
- `showLoadingSpinner?: boolean` - Show built-in loading spinner (default: true)
- `spinnerProps?: object` - Customize spinner appearance
#### `GradientProgressSpinner`
Standalone loading spinner component.
**Props:**
- `isLoading: boolean` - Whether to show spinner
- `size?: number` - Spinner size in pixels (default: 80)
- `strokeWidth?: number` - Stroke width (default: 4)
### Hooks
#### `useAppRouter()`
Main navigation hook.
**Returns:**
- `route: (path: string) => void` - Navigate function with query preservation
- `isNavigating: boolean` - Current navigation state
#### `useNavigateWithQuery()`
Low-level navigation hook.
**Returns:**
- `(path: string) => void` - Navigate function with query preservation
#### `useQueryParams()`
Get current query parameters as object.
**Returns:**
- `Record<string, string>` - Current query parameters
#### `useIsPathActive()`
Check if paths are active.
**Returns:**
- `(path: string, exact?: boolean) => boolean` - Path matching function
### Utilities
#### `navigateWithQuery(navigate, path, search)`
Navigate while preserving query parameters.
#### `withAppRouter(Component, options?)`
Higher-order component to wrap apps with router provider.