Server Side Rendering
Getting Started
1. How do I set up SSR with Next.js?
The React Search SDK supports both the App Router and Pages Router approaches for SSR implementation:
2. Can I use getStaticProps for search pages?
No, getStaticProps
is not suitable for search pages due to their dynamic nature:
Limitations:
- Cannot access dynamic URL parameters
- Cannot generate results for all possible parameter combinations
- No access to user-specific context
Key Considerations:
- Search pages depend on runtime factors:
- URL parameters (queries, filters, sorting, pagination)
- User context (preferences, location, personalization)
- Use
getServerSideProps
instead for:- Fresh search results
- Dynamic parameter handling
- User context access
- SEO benefits
3. Can I preload search data for better performance?
No, preloading search data is not recommended because:
- Search API responses are always fresh and updated
- Displaying preloaded data may show stale information
- Time-critical data (like product availability) requires real-time fetching
Common Questions
1. How do I handle server components vs client components?
Component organization in Next.js SSR:
-
Server Components
- Handle static content and SEO-critical elements
- Manage data fetching and initial page rendering
- Process server-side operations
-
Client Components
- Handle interactive elements and user interactions
- Manage dynamic UI updates
- Process client-side operations
Best Practices:
- Place the wrapper component in a separate file with
"use client"
directive - Import and render the wrapper in your route file
- Wrap all search-related components inside the wrapper
- Components become client-side but maintain server-rendered initial HTML
- Ensures search results are crawler-accessible
- Improves SEO performance
2. How do I pass initial data from server to client components?
Data flow in SSR implementation:
- Call the initialize function in your server component
- Pass the response to the SSR wrapper
- The wrapper creates a context for client components
- Access data in client components using hooks
Requirements:
- Mark client components with
"use client"
directive - Use appropriate hooks to access context data
- Maintain proper component hierarchy
For more informations, please refer to the initialise pages for the App Router and Pages Router.
3. Can I use React Server Components with this SDK?
No, the Unbxd React Search SDK is not directly compatible with React Server Components (RSC) due to its requirements for:
- Client-side state management
- Interactive UI elements
- Real-time event handling
- Browser APIs
Use client components for:
- The UnbxdSearchSSRWrapper and its children
- Interactive search features
- Dynamic UI updates
- User interaction handling
However, you can use RSC components outside the React Search Wrapper component in the following cases:
- Page layout and static content
- SEO-critical elements
- Initial data fetching
- Non-search related content
4. Why do my filters reset when navigating back?
According to the default implementation of the React Search SDK, the URL is added to the history stack only on initial load.
After that, any user interaction that changes the URL is not added to the history stack. Instead, it replaces the existing URL.
However, this implementation can be changed by using the setWebUrl
callback function.
5. What is the setWebUrl function? What value must I pass to it?
The setWebUrl
function is a configurable URL management function that handles how URL changes are reflected in the browser's history. It's part of the webUrlConfig configuration.
This is the default value:
const setWebUrl = (newUrl, redirect = false, replace = false) => {
if (replace) {
window.history.replaceState(null, "", newUrl);
} else {
window.history.pushState({}, "", newUrl);
}
};
newUrl
is the URL to be set.redirect
is a boolean value to indicate if the user should be redirected to the new URL.replace
is a boolean value to indicate if the new URL should replace the current URL in the history stack.
Here are some examples of values that can be passed to the setWebUrl
function in different frameworks:
Next.js - App Router:
import { useRouter } from "next/navigation";
const router = useRouter();
setWebUrl: (newUrl, redirect, replace) => {
if (redirect) {
// Hard navigation with page reload
window.location.href = newUrl;
} else if (replace) {
// Replace current entry in history
router.replace(newUrl);
} else {
// Push new entry to history
router.push(newUrl);
}
};
Next.js - Pages Router:
import { useRouter } from "next/router";
const router = useRouter();
setWebUrl: (newUrl, redirect, replace) => {
if (redirect) {
// Hard redirect
window.location.href = newUrl;
} else {
// Soft navigation
router.push(newUrl, undefined, {
shallow: false,
replace: replace,
});
}
};
App Router
1. Where should I place the initialize function call?
"use client"
directive at the top.The initialize function must be called to fetch initial search results, ensuring server-rendered content is available for SEO.
App Router Implementation:
- Place the initialize function call in
page.tsx
- Pass the initialization response as props to the wrapper component
- Import and render the wrapper component in
page.tsx
2. How do I pass the current path to the initialize function?
To make use of the current path in the initialise function, you need to make use of middlewares. Middlewares are a way to modify the request and response in Next.js.
You can make use of the headers
object to get the current path.
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const headers = new Headers(request.headers);
// Extract search parameters from URL
headers.set("x-path-name", request.nextUrl.href.split("?")[1]);
return NextResponse.next({ headers });
}
// app/search/page.tsx
import { headers } from "next/headers";
import { initialise } from "@unbxd-ui/react-search-hooks/ssr-utilities";
export default async function Search() {
const headerList = headers();
const currentPath = headerList.get("x-path-name") || "";
const initialData = await initialise({
apiKey: "your-api-key",
siteKey: "your-site-key",
currentPath,
});
return <SearchWrapper initialData={initialData}>{/* Your components here */}</SearchWrapper>;
}
Pages Router
1. Where should I place the initialize function call?
Pages Router Implementation:
- Place the initialize function call in
getServerSideProps
withinindex.tsx
- Pass the initialization response as props to the wrapper component
Example implementation:
// pages/search/[category]/index.tsx
import { initialise } from "@unbxd-ui/react-search-hooks/ssr-utilities";
export const getServerSideProps = async (context: { resolvedUrl: string; params: { category: string } }) => {
const currentPath = context.resolvedUrl;
const { category } = context.params; // Dynamic route parameter
const initialData = await initialise({
apiKey: "your-api-key",
siteKey: "your-site-key",
currentPath,
});
return {
props: { initialData },
};
};
export default function Search({ initialData }: { initialData: any }) {
return <SearchWrapper initialData={initialData}>{/* Your components here */}</SearchWrapper>;
}
Troubleshooting
1. Why am I getting hydration mismatches?
Common causes and solutions for hydration mismatches:
-
Using CSR Wrapper Instead of SSR Wrapper
- The CSR wrapper performs initial data fetching on the client side
- This creates a mismatch between server and client HTML
- Solution: Use the SSR wrapper for server-side data fetching
-
Improper Component Setup
- Ensure wrapper component is marked with
"use client"
- Keep consistent state between server and client
- Follow proper component hierarchy
- Ensure wrapper component is marked with
2. How do I resolve 'window is not defined' errors?
These errors occur due to server-side and client-side code execution mismatches. Here are common scenarios and solutions:
- Client-Side Code in Server Components:
// ❌ Incorrect: Direct window usage
function SearchComponent() {
const windowWidth = window.innerWidth; // Causes error
return <div>{windowWidth}</div>;
}
// âś… Correct: Safe window usage with useEffect
function SearchComponent() {
const [windowWidth, setWindowWidth] = useState(0);
useEffect(() => {
setWindowWidth(window.innerWidth);
}, []);
return <div>{windowWidth}</div>;
}
- Component Setup:
// ❌ Incorrect: Missing 'use client' directive
import { UnbxdSearchSSRWrapper } from "@unbxd-ui/react-search-hooks";
export function Wrapper({ initialData }) {
return <UnbxdSearchSSRWrapper initialData={initialData}>{/* Components */}</UnbxdSearchSSRWrapper>;
}
// âś… Correct: With 'use client' directive
("use client");
import { UnbxdSearchSSRWrapper } from "@unbxd-ui/react-search-hooks";
export function Wrapper({ initialData }) {
return <UnbxdSearchSSRWrapper initialData={initialData}>{/* Components */}</UnbxdSearchSSRWrapper>;
}
Always mark the UnbxdSearchSSRWrapper and its child components as client-side components using the 'use client' directive in separate files from server components.
3. How do I debug SSR vs CSR rendering differences?
Use these debugging techniques to identify and resolve rendering inconsistencies:
-
Event Monitoring
- Use the
onEvent
callback to log events and data flow - Monitor both server and client-side events
- Use the
-
Development Tools
- React DevTools for component hierarchy inspection
- Browser DevTools for network requests
- Server-side logs for initialization issues
-
Common Checks
- Verify hydration warnings in console
- Compare initial data between server and client
- Check component mounting sequence
- Monitor state changes during hydration
-
Environment-Specific Logging
if (typeof window !== "undefined") { console.log("Client-side:", data); } else { console.log("Server-side:", data); }
-
Network Analysis
- Monitor API requests timing
- Check response consistency
- Verify data synchronization