Minimalistic React SSR Framework
This challenge asks you to build a simplified Server-Side Rendering (SSR) framework for React applications using TypeScript. SSR improves initial load times and SEO by rendering the application on the server before sending the HTML to the client. This framework will focus on the core concepts of SSR, handling routing and component rendering on the server.
Problem Description
You are tasked with creating a basic SSR framework that can render React components based on a given route. The framework should:
- Route Handling: Accept a route string (e.g.,
/about,/products/123) and determine which component to render. For simplicity, assume a direct mapping between routes and components. - Component Rendering: Render the appropriate React component to a string. This string will be the HTML sent to the client.
- Context Provision: Provide a minimal context object to the React components during server-side rendering. This context should include a
urlproperty representing the current route. - Error Handling: Gracefully handle cases where a route doesn't map to a component.
Key Requirements:
- The framework should be written in TypeScript.
- It should be modular and relatively easy to extend.
- It should not rely on any external SSR libraries (e.g., Next.js, Remix). The goal is to understand the underlying principles.
- The framework should be able to handle dynamic routes (e.g.,
/products/:id). For this challenge, dynamic route parameters are not required to be parsed; simply match the route prefix.
Expected Behavior:
Given a route and a set of registered components, the framework should return an HTML string representing the rendered component for that route. If the route is not found, it should return a simple "404 Not Found" message.
Edge Cases to Consider:
- Empty route string (
/). - Routes with query parameters (e.g.,
/products?sort=price). Ignore query parameters for this challenge. - Routes that are prefixes of other routes (e.g.,
/and/about). The most specific route should be matched.
Examples
Example 1:
Input: route = "/about", components = { "/about": () => <h1>About Us</h1>, "/home": () => <h1>Home</h1> }
Output: <h1>About Us</h1>
Explanation: The route "/about" is found in the components map, so the corresponding component is rendered.
Example 2:
Input: route = "/home", components = { "/about": () => <h1>About Us</h1>, "/home": () => <h1>Home</h1> }
Output: <h1>Home</h1>
Explanation: The route "/home" is found in the components map, so the corresponding component is rendered.
Example 3:
Input: route = "/contact", components = { "/about": () => <h1>About Us</h1>, "/home": () => <h1>Home</h1> }
Output: "404 Not Found"
Explanation: The route "/contact" is not found in the components map, so the "404 Not Found" message is returned.
Example 4:
Input: route = "/", components = { "/": () => <h1>Home</h1>, "/about": () => <h1>About Us</h1> }
Output: <h1>Home</h1>
Explanation: The route "/" is found and rendered.
Constraints
- The
routestring will only contain alphanumeric characters,/, and?. - The
componentsobject will contain string keys (routes) and function values (React components). - React components should be functional components that return JSX.
- The framework should be able to handle at least 5 registered components.
- Performance is not a primary concern for this challenge, but avoid excessively inefficient operations.
Notes
- You'll need to use a library like
ReactDOMServerto render React components to a string. Make sure to install it:npm install react-dom. - Consider using a simple routing algorithm to match routes to components. A straightforward approach is to iterate through the components and check for exact matches or prefix matches.
- The
contextobject is a minimal requirement. You can extend it with additional properties as needed. - Focus on the core SSR concepts: routing and component rendering on the server. Don't worry about advanced features like hot module replacement or code splitting.
- Think about how you would structure your code to make it reusable and testable.