This guide explains how to fix the navigation and URL handling issues when using @simtlix/simfinity-fe-components with Next.js.
The components were using direct window.location APIs which cause issues in:
- Server-side rendering (SSR)
- Next.js App Router
- Other React frameworks
- When components are used in different environments
The components now accept optional navigation and URL handling props that allow you to provide your own implementation.
type EntityTableProps = {
listField: string;
// Optional navigation and URL handling
onNavigate?: (path: string) => void;
getSearchParams?: () => URLSearchParams;
onSearchParamsChange?: (params: URLSearchParams) => void;
};type EntityFormProps = {
listField: string;
entityId?: string;
action: "create" | "edit" | "view";
// Optional navigation handling
onNavigate?: (path: string) => void;
};// hooks/useNavigation.ts
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import { useCallback } from 'react';
export function useNavigation() {
const router = useRouter();
const searchParams = useSearchParams();
const navigate = useCallback((path: string) => {
router.push(path);
}, [router]);
const getSearchParams = useCallback(() => {
return searchParams;
}, [searchParams]);
const onSearchParamsChange = useCallback((params: URLSearchParams) => {
const newUrl = `${window.location.pathname}?${params.toString()}`;
router.replace(newUrl);
}, [router]);
return {
navigate,
getSearchParams,
onSearchParamsChange,
};
}// app/entities/page.tsx
'use client';
import { EntityTable, EntityForm } from '@simtlix/simfinity-fe-components';
import { useNavigation } from '@/hooks/useNavigation';
export default function EntitiesPage() {
const { navigate, getSearchParams, onSearchParamsChange } = useNavigation();
return (
<div>
<EntityTable
listField="series"
onNavigate={navigate}
getSearchParams={getSearchParams}
onSearchParamsChange={onSearchParamsChange}
/>
</div>
);
}// app/entities/create/page.tsx
'use client';
import { EntityForm } from '@simtlix/simfinity-fe-components';
import { useNavigation } from '@/hooks/useNavigation';
export default function CreateEntityPage() {
const { navigate } = useNavigation();
return (
<EntityForm
listField="series"
action="create"
onNavigate={navigate}
/>
);
}// hooks/useNavigation.ts
import { useRouter } from 'next/router';
import { useCallback } from 'react';
export function useNavigation() {
const router = useRouter();
const navigate = useCallback((path: string) => {
router.push(path);
}, [router]);
const getSearchParams = useCallback(() => {
return new URLSearchParams(router.asPath.split('?')[1] || '');
}, [router.asPath]);
const onSearchParamsChange = useCallback((params: URLSearchParams) => {
const newUrl = `${router.pathname}?${params.toString()}`;
router.replace(newUrl);
}, [router]);
return {
navigate,
getSearchParams,
onSearchParamsChange,
};
}// pages/entities/index.tsx
import { EntityTable } from '@simtlix/simfinity-fe-components';
import { useNavigation } from '@/hooks/useNavigation';
export default function EntitiesPage() {
const { navigate, getSearchParams, onSearchParamsChange } = useNavigation();
return (
<div>
<EntityTable
listField="series"
onNavigate={navigate}
getSearchParams={getSearchParams}
onSearchParamsChange={onSearchParamsChange}
/>
</div>
);
}// hooks/useNavigation.ts
import { useNavigate, useLocation } from 'react-router-dom';
import { useCallback } from 'react';
export function useNavigation() {
const navigate = useNavigate();
const location = useLocation();
const handleNavigate = useCallback((path: string) => {
navigate(path);
}, [navigate]);
const getSearchParams = useCallback(() => {
return new URLSearchParams(location.search);
}, [location.search]);
const onSearchParamsChange = useCallback((params: URLSearchParams) => {
const newUrl = `${location.pathname}?${params.toString()}`;
navigate(newUrl, { replace: true });
}, [navigate, location.pathname]);
return {
navigate: handleNavigate,
getSearchParams,
onSearchParamsChange,
};
}If you don't provide the navigation props, the components will fall back to using window.location APIs:
// This will work in browser environments
<EntityTable listField="series" />
<EntityForm listField="series" action="create" />// This would cause SSR issues
<EntityTable listField="series" />
<EntityForm listField="series" action="create" />// Next.js App Router
const { navigate, getSearchParams, onSearchParamsChange } = useNavigation();
<EntityTable
listField="series"
onNavigate={navigate}
getSearchParams={getSearchParams}
onSearchParamsChange={onSearchParamsChange}
/>
<EntityForm
listField="series"
action="create"
onNavigate={navigate}
/>- SSR Compatible - No more server-side rendering issues
- Framework Agnostic - Works with any routing solution
- Backward Compatible - Still works without props in browser environments
- Type Safe - Full TypeScript support
- Flexible - You control the navigation behavior
Solution: Make sure you're passing the navigation props
Solution: Check that onSearchParamsChange is properly implemented
Solution: Verify that onNavigate is calling your router correctly
This fix ensures your components work properly in all React environments! 🎉