Skip to main content
RapidDev - Software Development Agency
lovable-issues

Using Dynamic Routes in Lovable Without Errors

Dynamic routes in Lovable use React Router's :param syntax (like /users/:id) combined with the useParams hook to read the URL parameter. Common failures include missing the Route definition in App.tsx, not handling loading and not-found states for invalid IDs, and forgetting SPA fallback configuration on deployed hosting platforms.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read~15 minAll Lovable projects using React RouterMarch 2026RapidDev Engineering Team
TL;DR

Dynamic routes in Lovable use React Router's :param syntax (like /users/:id) combined with the useParams hook to read the URL parameter. Common failures include missing the Route definition in App.tsx, not handling loading and not-found states for invalid IDs, and forgetting SPA fallback configuration on deployed hosting platforms.

Why dynamic routes fail or show blank pages in Lovable

Dynamic routes let you use URL parameters (like /products/42 or /blog/my-first-post) to load different content from the same page component. Lovable projects use React Router with BrowserRouter, which handles dynamic segments via the :param syntax. The most common failure is a missing Route definition. Your page component exists and uses useParams to read the ID, but App.tsx does not have a Route entry with the :param segment. React Router does not know this path exists, so it falls through to a catch-all or shows a blank page. Another frequent issue is not handling invalid parameters. When someone visits /users/abc and your code expects a numeric ID, the Supabase query returns no results but the component tries to render the data anyway, causing 'cannot read property of undefined' errors. Always validate the parameter and show a 404 state when the data does not exist. Finally, dynamic routes that work in Lovable's preview may break on deployed hosts (Vercel, Netlify) because the server does not know about /users/42 — it only knows about /index.html. This is the SPA routing fallback issue, covered in detail on our SPA fallback configuration page.

  • Missing Route definition in App.tsx — the path with :param is not registered in the router
  • useParams returns undefined — the component is rendered outside a Route or the param name does not match
  • No loading state — the component renders before the data fetch completes, causing undefined errors
  • Invalid parameter not handled — visiting /users/abc when only numeric IDs exist produces a broken page
  • SPA fallback not configured on the hosting platform — direct URL access returns a server 404

Error messages you might see

TypeError: Cannot read properties of undefined (reading 'name')

The component is trying to access a property on data that has not loaded yet, or the parameter does not match any record. Add a loading check and a not-found fallback.

No routes matched location "/users/42"

React Router cannot find a Route definition that matches this URL. Add a Route with path='/users/:id' in your App.tsx file.

useParams() may only be used in the context of a <Router> component

The component using useParams is rendered outside the BrowserRouter. Make sure all page components are inside the Routes component within BrowserRouter.

Before you start

  • A Lovable project with React Router configured (this is the default for all Lovable projects)
  • A page component that should display data based on a URL parameter
  • A data source (Supabase table or API) that the component will query using the URL parameter

How to fix it

1

Add the dynamic Route definition in App.tsx

React Router needs a Route entry with the :param syntax to know which component to render for dynamic URLs

Open src/App.tsx in Dev Mode or prompt Lovable to edit it. Add a Route element with a path that includes the dynamic segment. The colon prefix (:id) tells React Router this is a variable. The component assigned to this route can then read the value using useParams().

Before
typescript
<Routes>
<Route path="/" element={<Index />} />
<Route path="/users" element={<UserList />} />
</Routes>
After
typescript
<Routes>
<Route path="/" element={<Index />} />
<Route path="/users" element={<UserList />} />
<Route path="/users/:id" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Routes>

Expected result: Visiting /users/42 renders the UserProfile component, and the wildcard * route catches any undefined paths.

2

Read the URL parameter with useParams and fetch data

useParams extracts the dynamic segment from the URL so you can use it to query your data source

In your page component, call useParams() to get the parameter value. The key name must match what you defined in the Route path — if the path is /users/:id, then useParams returns an object with an id property. Use this value to fetch the matching record from Supabase.

Before
typescript
function UserProfile() {
// No parameter reading — hardcoded user ID
const [user, setUser] = useState(null);
useEffect(() => {
supabase.from("users").select("*").eq("id", 1).single();
}, []);
return <div>{user?.name}</div>;
}
After
typescript
import { useParams } from "react-router-dom";
function UserProfile() {
const { id } = useParams<{ id: string }>();
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (!id) return;
supabase
.from("users")
.select("*")
.eq("id", id)
.single()
.then(({ data, error }) => {
if (!error) setUser(data);
setIsLoading(false);
});
}, [id]); // Re-fetch when the URL parameter changes
if (isLoading) return <p>Loading...</p>;
if (!user) return <p>User not found.</p>;
return <div>{user.name}</div>;
}

Expected result: The component reads the ID from the URL, fetches the matching record, and displays it. Loading and not-found states are handled.

3

Handle invalid parameters and show a 404 state

Users can type anything in the URL — your component must handle IDs that do not match any record gracefully

Add validation for the URL parameter before making the data request. If the parameter format is wrong (letters where you expect numbers) or the database query returns no results, show a user-friendly not-found message instead of a broken page. Link back to the list page so users can navigate away easily.

Before
typescript
// No validation — crashes on invalid ID
useEffect(() => {
supabase.from("products").select("*").eq("id", id).single()
.then(({ data }) => setProduct(data));
}, [id]);
After
typescript
useEffect(() => {
if (!id) {
setError("No product ID provided");
setIsLoading(false);
return;
}
// Validate that the ID looks like a UUID or number
const isValidId = /^[0-9a-f-]{36}$/.test(id) || /^\d+$/.test(id);
if (!isValidId) {
setError("Invalid product ID format");
setIsLoading(false);
return;
}
supabase
.from("products")
.select("*")
.eq("id", id)
.single()
.then(({ data, error }) => {
if (error || !data) {
setError("Product not found");
} else {
setProduct(data);
}
setIsLoading(false);
});
}, [id]);

Expected result: Invalid IDs show a friendly 'not found' message instead of a crash. Valid IDs load the correct data.

4

Link to dynamic routes using the Link component

Using Link instead of anchor tags enables client-side navigation without a full page reload

In your list page, use React Router's Link component to navigate to dynamic routes. Construct the URL by interpolating the item's ID. This keeps navigation within the SPA and preserves app state. If you need programmatic navigation (like after a form submission), use the useNavigate hook instead. For projects with complex routing across many generated pages, RapidDev's engineers have built robust navigation patterns across 600+ Lovable projects.

Before
typescript
<ul>
{users.map(user => (
<li key={user.id}>
<a href={`/users/${user.id}`}>{user.name}</a>
</li>
))}
</ul>
After
typescript
import { Link } from "react-router-dom";
<ul>
{users.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`} className="text-primary hover:underline">
{user.name}
</Link>
</li>
))}
</ul>

Expected result: Clicking a user name navigates to /users/[id] without a full page reload, and the browser back button works correctly.

Complete code example

src/pages/UserProfile.tsx
1import { useEffect, useState } from "react";
2import { useParams, Link } from "react-router-dom";
3import { supabase } from "@/integrations/supabase/client";
4
5interface UserData {
6 id: string;
7 name: string;
8 email: string;
9 avatar_url: string | null;
10 created_at: string;
11}
12
13export default function UserProfile() {
14 const { id } = useParams<{ id: string }>();
15 const [user, setUser] = useState<UserData | null>(null);
16 const [isLoading, setIsLoading] = useState(true);
17 const [error, setError] = useState<string | null>(null);
18
19 useEffect(() => {
20 if (!id) {
21 setError("No user ID provided in the URL");
22 setIsLoading(false);
23 return;
24 }
25
26 supabase
27 .from("profiles")
28 .select("id, name, email, avatar_url, created_at")
29 .eq("id", id)
30 .single()
31 .then(({ data, error: dbError }) => {
32 if (dbError || !data) {
33 setError("User not found");
34 } else {
35 setUser(data);
36 }
37 setIsLoading(false);
38 });
39 }, [id]);
40
41 if (isLoading) {
42 return <div className="p-8 text-muted-foreground">Loading profile...</div>;
43 }
44
45 if (error) {
46 return (
47 <div className="p-8 text-center">
48 <h2 className="text-xl font-semibold mb-2">{error}</h2>
49 <Link to="/users" className="text-primary hover:underline">
50 Back to user list
51 </Link>
52 </div>
53 );
54 }
55
56 return (
57 <div className="p-8 max-w-2xl mx-auto">
58 <Link to="/users" className="text-sm text-muted-foreground hover:underline mb-4 block">
59 &larr; All users
60 </Link>
61 <h1 className="text-2xl font-bold mb-4">{user?.name}</h1>
62 <p className="text-muted-foreground">{user?.email}</p>
63 </div>
64 );
65}

Best practices to prevent this

  • Always define a catch-all Route (path='*') in App.tsx so unmatched URLs show a proper 404 page instead of a blank screen
  • Use useParams with TypeScript generics (useParams<{ id: string }>()) for type safety on parameter names
  • Add id to the useEffect dependency array so the component re-fetches when the URL parameter changes
  • Validate URL parameters before making database queries — show a not-found state for invalid formats
  • Use Link from react-router-dom instead of HTML anchor tags to keep navigation within the SPA
  • Always include loading and error states in components that fetch data by URL parameter
  • Configure SPA fallback routing on your hosting platform so direct URL access to dynamic routes works after deployment

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Lovable.dev project and I need to create a dynamic route that shows different content based on the URL. For example, /products/42 should show product #42. Here is my current App.tsx routing setup: [paste your Routes from App.tsx] Here is the component that should display the dynamic content: [paste your page component] Please help me: 1. Add the correct Route definition with a :param segment 2. Use useParams to read the parameter 3. Fetch the matching data from Supabase 4. Handle loading, not-found, and invalid parameter states

Lovable Prompt

I need a dynamic route at /products/:id that loads a product from the products Supabase table and displays its name, price, and description. Add the Route definition in @src/App.tsx and create a new ProductDetail page component in @src/pages/ProductDetail.tsx. Include loading and not-found states. Use the Link component in @src/pages/Products.tsx to link each product to its detail page.

Frequently asked questions

How do I create a dynamic route in Lovable?

Add a Route in App.tsx with a colon-prefixed parameter: <Route path='/users/:id' element={<UserProfile />} />. In the UserProfile component, call useParams() to read the id value from the URL. Use that value to fetch data from Supabase.

Why does my dynamic route show a blank page?

The most likely cause is a missing Route definition in App.tsx. Check that you have a Route with the :param syntax matching the URL pattern. Also verify the component has proper loading and error states so it does not render before data is available.

How do I handle invalid URL parameters?

Validate the parameter format before making a database query. If the format is wrong or the query returns no results, set an error state and display a not-found message with a link back to the list page. Never assume the parameter will always be valid.

Can I use slugs instead of IDs in the URL?

Yes. Define the route as /blog/:slug and query your Supabase table using .eq('slug', slug) instead of .eq('id', id). Slugs are more user-friendly in URLs but must be unique in your database. Add a unique constraint on the slug column.

Why do dynamic routes work in preview but 404 after deployment?

Lovable's preview has built-in SPA routing support, but external hosts (Vercel, Netlify) need explicit configuration. Add a vercel.json with rewrites or a _redirects file so the server routes all paths to index.html.

How do I pass data between a list page and a detail page?

The recommended approach is to pass only the ID in the URL and fetch the full data on the detail page. This ensures the detail page works on direct access and page refresh. Avoid passing data through React Router state since it is lost on refresh.

What if I can't fix this myself?

If your app needs complex routing with nested dynamic segments, authentication-gated routes, or data prefetching strategies, RapidDev's engineers have built these patterns across 600+ Lovable projects and can implement a robust solution.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.