# How to Add a Dynamic Map Route Planner to Your App — Copy-Paste Prompts Inside

- Tool: App Features
- Last updated: July 2026

## TL;DR

A dynamic route planner needs four pieces: a waypoint input with address autocomplete, a Directions API call proxied through an Edge Function to hide your key, a route polyline drawn on the map, and an optional turn-by-turn instruction panel. With Lovable or V0 you can ship a working A-to-B web planner in 8–14 hours. The Mapbox Directions API is free up to 100K requests per month — costs only appear past 10K active users.

## What a Dynamic Map Route Planner Actually Is

A route planner turns your map from a static display into a navigation tool: users enter an origin and destination, the app draws a coloured polyline showing the fastest path, and a panel lists every turn they need to make. The feature lives on top of a base map (Mapbox or Google Maps) and calls a Directions API to compute the route geometry. The real product decisions are multi-stop support, travel mode (driving, walking, cycling), re-routing when the user goes off-course, and whether routes should be saved to user history. AI tools handle the A-to-B case well; re-routing and waypoint reordering are where they need manual help.

## Anatomy of the Feature

Six components — the waypoint input and API proxy are where AI builds most often fall short. Get those right and everything else assembles quickly.

- **Waypoint input with autocomplete** (ui): Origin, destination, and optional intermediate stop fields with address autocomplete. On web: shadcn/ui Combobox wired to the Mapbox Search Box JS SDK or Google Places Autocomplete JS library. On Flutter: TypeAheadField widget (flutter_typeahead package) with a debounced HTTP call to the autocomplete endpoint — debounce at 300ms so you are not firing on every keystroke.
- **Directions API proxy (Edge Function)** (backend): A Supabase Edge Function that receives sanitised waypoints from the frontend, calls the Mapbox Directions API (or Google Maps Directions API) using a secret key stored in Deno.env.get('MAPBOX_SECRET_TOKEN'), and returns the GeoJSON LineString response. Profiles supported: mapbox/driving, mapbox/walking, mapbox/cycling. Never expose the secret key in client-side code — network tab inspection takes seconds.
- **Route renderer** (ui): Draws the route polyline over the base map tiles. On web with Mapbox GL JS: addSource() with a GeoJSON LineString, then addLayer() with a line type — colour, width, and optional dash pattern set here. On Flutter with flutter_map: PolylineLayer that accepts a list of LatLng coordinates decoded from the Directions API response. The route line should be visually distinct from the base map — 5px width, brand colour.
- **Turn-by-turn instruction panel** (ui): An ordered list of maneuver steps from the Directions API 'legs[].steps[]' response. Each step has a maneuver type (turn-left, turn-right, straight, arrive), distance, and instruction text. On Flutter: ListView.builder with an icon from a maneuver-type → icon map. On web: a scrollable React list. The current active step should highlight as the user progresses along the route.
- **Route state store** (data): Client-side state holding: origin (name + lat/lng), destination (name + lat/lng), waypoints array, selected travel mode, the fetched route GeoJSON, and the decoded steps array. In React: useState or Zustand. In Flutter: Provider or Riverpod page state. Optionally persisted to Supabase 'saved_routes' table so the user can revisit past routes from their history screen.
- **Re-routing engine (mobile)** (backend): Mobile-only: a Dart stream from the geolocator package compares the user's current LatLng to the nearest point on the route polyline. When deviation exceeds 50 metres, the app calls the Edge Function again with the current GPS position as the new origin and the remaining waypoints. The geodesy package provides the Haversine distance calculation. On web, re-routing is not applicable.

## Data model

The data model is optional — a basic route planner works entirely with client-side state. Add the 'saved_routes' table if users need route history. Run this SQL in the Supabase SQL editor (Database → SQL Editor → New query):

```sql
create table public.saved_routes (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  origin_name text not null,
  destination_name text not null,
  waypoints jsonb not null default '[]'::jsonb,
  mode text not null default 'driving' check (mode in ('driving','walking','cycling')),
  distance_m int,
  duration_s int,
  created_at timestamptz not null default now()
);

alter table public.saved_routes enable row level security;

create policy "Users can view own saved routes"
  on public.saved_routes for select
  using (auth.uid() = user_id);

create policy "Users can insert own saved routes"
  on public.saved_routes for insert
  with check (auth.uid() = user_id);

create policy "Users can delete own saved routes"
  on public.saved_routes for delete
  using (auth.uid() = user_id);

create index saved_routes_user_recent_idx
  on public.saved_routes (user_id, created_at desc);
```

The waypoints column stores intermediate stops as a JSONB array of {name, lat, lng} objects. The index on (user_id, created_at desc) keeps the history screen fast even after a user accumulates hundreds of saved routes.

## Build paths

### Lovable — fit 3/10, 8–12 hours

Best for a web route planner that is part of a larger Lovable app — generates the Mapbox GL JS embed, origin/destination form, and Edge Function proxy in one project. Multi-waypoint reordering and re-routing require manual follow-up.

1. Create a new Lovable project and open the Cloud tab to add MAPBOX_PUBLIC_TOKEN and MAPBOX_SECRET_TOKEN to Secrets
2. Paste the prompt below in Agent Mode — it specifies the Edge Function, polyline render, mode toggle, and saved-routes flow
3. Once generated, open the Supabase Edge Function log in the Cloud tab to confirm the Directions API call returns a 200 and not a 401 (wrong token) or 422 (malformed waypoints)
4. Publish to production URL — the map container collapses to 0px inside the preview iframe; route rendering only works on the published HTTPS URL
5. Test deeplink sharing by copying the URL with ?origin= and ?destination= params and opening it in a new tab

Starter prompt:

```
Build a dynamic route planner page using Mapbox GL JS. Show a base map centred on the user's browser location. Add an input panel with: an origin field (default to user's location), a destination field, up to 3 intermediate waypoint fields (add/remove buttons), and a travel mode selector (driving, walking, cycling). When the user submits, call a Supabase Edge Function named 'get-directions' that proxies the Mapbox Directions API using MAPBOX_SECRET_TOKEN from Deno.env — never call the Mapbox API directly from the frontend. Draw the returned GeoJSON LineString as a 5px coloured polyline on the map. Fit the map viewport to the route bounding box. Show a turn-by-turn instruction list below the map with step icons and distances. Show estimated total time and distance above the instruction list before the user commits. Add a 'Save Route' button that inserts into Supabase saved_routes (origin_name, destination_name, waypoints jsonb, mode, distance_m, duration_s) for the authenticated user with RLS enabled. Add a 'My Routes' tab showing saved routes newest first. Encode all waypoints in the page URL as query params so the route can be shared by link. Handle no-route-found error (e.g. ferry-only routes) with a friendly message. Show a loading skeleton while the route fetches.
```

Limitations:

- Lovable is Vite/React (web only) — no native mobile GPS or re-routing
- The map container renders blank in the Lovable preview iframe; always test on the published URL
- Complex waypoint drag-to-reorder may need a manual code edit after first generation
- Directions API key exposure risk is high if 'proxy via Edge Function' is not explicitly in the prompt

### Flutterflow — fit 4/10, 10–14 hours

The right path for a native mobile route planner — flutter_map renders the polyline natively and geolocator provides real GPS for origin detection. Re-routing and coordinate extraction from the API response both require custom Dart action blocks.

1. Add the flutter_map package in FlutterFlow via Settings → Pubspec Dependencies; add the FlutterMap widget to your route page and set the tile layer URL to OpenStreetMap or configure a Mapbox tile URL with your public token
2. Add a TextField for destination input; in the Action editor, wire an HTTP GET action to your Supabase Edge Function URL (get-directions) passing origin lat/lng, destination lat/lng, waypoints, and mode as query params
3. Add a Custom Action (Dart) to parse the Directions API response: jsonDecode the body, extract geometry.coordinates as a List<LatLng>, and update a Page State variable holding the polyline points
4. Bind the Page State polyline points to a PolylineLayer in the FlutterMap widget; set strokeWidth to 5.0 and color to your brand colour
5. Add a ListView bound to the legs[0].steps array from the API response for the turn-by-turn panel; use conditional icons mapped to maneuver type
6. In Settings → App Permissions, enable Location When In Use (iOS and Android) with a descriptive usage message for App Store review

Limitations:

- Re-routing on GPS deviation requires a Dart custom action with a geolocator stream — not achievable in the visual action editor alone
- The Directions API response coordinate extraction (nested arrays) fails silently if done with FlutterFlow's built-in JSON path — always use a Custom Action for this
- Directions API key must be stored as a Supabase secret and proxied via Edge Function, not hardcoded in the FlutterFlow HTTP action URL
- Offline route caching is not supported without significant custom code

### V0 — fit 4/10, 6–10 hours

Best for web route planning dashboards or logistics admin panels built on Next.js — V0 generates clean Mapbox GL JS components and Server Actions that proxy the Directions API, and the Vars panel handles environment variables natively.

1. Prompt V0 with the spec below; it will scaffold a Next.js route planner with a Mapbox GL JS map component and a Server Action for the Directions API proxy
2. In the V0 Vars panel, add NEXT_PUBLIC_MAPBOX_TOKEN (public, for map tiles) and MAPBOX_SECRET_TOKEN (server-only, for Directions API); never prefix the secret token with NEXT_PUBLIC_
3. Run the SQL schema from this page in the Supabase SQL editor, then add NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY to the Vars panel
4. Publish to production via the V0 Git panel; test the route rendering and deeplink sharing on the live URL

Starter prompt:

```
Build a Next.js route planner page at /route-planner using Mapbox GL JS (mapbox-gl npm). Map component: client component, centred on user's browser geolocation on load, explicit height of 500px. Waypoint panel: origin field, destination field, up to 3 intermediate stops with add/remove, travel mode toggle (driving/walking/cycling). On submit, call a Next.js Server Action that proxies the Mapbox Directions API using process.env.MAPBOX_SECRET_TOKEN — never call Mapbox directly from the client. Draw the returned GeoJSON LineString as a 5px polyline using map.addSource + addLayer. Fit the viewport to the route bounds using map.fitBounds. Show estimated time (minutes) and distance (km) in a summary bar above the map. Render a scrollable turn-by-turn instruction list below the map with maneuver icons. Save route on demand to Supabase saved_routes table via Server Action (RLS: user sees only own rows). Encode waypoints in URL query params for shareability. Handle no-route error and API loading state with skeleton. Guard against localStorage access during SSR — use Supabase Server Action for saved routes, not localStorage.
```

Limitations:

- Web only — no native GPS tracking, no re-routing, no mobile-native polyline rendering
- Mapbox GL JS adds approximately 300KB to the Next.js page bundle — consider dynamic import with ssr: false
- V0 occasionally uses localStorage for route caching which throws on Next.js server render — the prompt explicitly guards against this but check the generated code
- shadcn/ui registry mismatches may require a follow-up 'fix the imports' prompt for the Combobox component

### Custom — fit 5/10, 2–4 weeks

Required when routing is the core product: fleet tracking with live deviation alerts, isochrone maps, route optimisation across 10+ waypoints, or offline route caching for expeditions and fieldwork.

1. Flutter app: flutter_map + Mapbox Directions API + geodesy package for Haversine deviation checks; geolocator stream for real-time GPS; Workmanager for background position tracking so re-routing continues when the app is backgrounded
2. Supabase saved_routes table with full GeoJSON stored as jsonb for route replay; PostGIS for spatial queries like 'find all routes that pass through this bounding box'
3. For 10+ waypoint optimisation: Mapbox Optimization API (not the standard Directions API) or a custom TSP solver on the server — reduces driving distance by 20–40% for delivery runs
4. Offline route caching: download and store MBTiles for a bounding box via Mapbox Offline Downloads SDK; store route GeoJSON in local SQLite so navigation continues with no connectivity

Limitations:

- Re-routing with smooth polyline transition requires careful state management — naively replacing the source causes a flash
- App Store and Google Play review is more thorough for apps using background location — budget 2–4 additional weeks for the review cycle
- Mapbox Optimization API costs more than the Directions API and has a 12-waypoint limit per request

## Gotchas

- **Mapbox API key exposed in browser network requests** — AI tools frequently generate a direct fetch to api.mapbox.com or maps.googleapis.com from client-side React or Flutter code. The secret API key appears in the browser's DevTools network tab and in the compiled app binary, and can be extracted and abused by anyone who inspects the page. Fix: Always route Directions API calls through a Supabase Edge Function. The frontend sends only the sanitised waypoint list; the Edge Function reads the secret key from Deno.env.get('MAPBOX_SECRET_TOKEN') and returns the route GeoJSON. Say 'proxy via Edge Function, never call Mapbox from the frontend' explicitly in your prompt.
- **Route polyline renders blank in Lovable preview** — Lovable's preview runs inside a cross-origin iframe. Mapbox GL JS requires a real DOM container with a known width and height to initialise the WebGL canvas. Inside the iframe the container collapses to 0px height, the WebGL context fails to create, and the map renders as a blank white box with no error in the console. Fix: Add an explicit height (e.g. height: 500px) to the map container div in your CSS. Then publish to the production URL and test there — the map and route polyline will render correctly on the published HTTPS domain.
- **FlutterFlow HTTP action returns 200 but the polyline is empty** — The Mapbox Directions API returns a deeply nested JSON structure: routes[0].geometry.coordinates is an array of [lng, lat] pairs. FlutterFlow's built-in JSON path extractor cannot reliably navigate this nesting, so the PolylineLayer points variable stays empty and nothing draws on the map — and no error is thrown. Fix: Use a Custom Action (Dart) for the response parsing step. Call jsonDecode on the raw response body, navigate routes[0].geometry.coordinates manually, convert each [lng, lat] pair to a LatLng object (note: Mapbox returns longitude first), and set the result into a Page State variable bound to your PolylineLayer.
- **Multi-waypoint reordering breaks the route order** — When users drag to reorder intermediate stops, AI-generated code often fires the Directions API call before the waypoints state has been re-indexed, sending the old order. The route displayed no longer matches the waypoint list the user sees, which is confusing and potentially dangerous for navigation. Fix: Rebuild the full waypoints array from the new sort order before triggering the Edge Function call. Use a keyed list (React key or Flutter ValueKey) to track identity across reorders, and debounce the API call by 500ms so rapid drags don't fire multiple requests.
- **V0 generates localStorage for saved routes, crashing on Next.js SSR** — V0 sometimes reaches for localStorage to cache saved routes between sessions. On Next.js, any component that accesses localStorage during server render throws 'ReferenceError: localStorage is not defined', breaking the entire page for all users who don't have a client-side hydration yet. Fix: Replace localStorage with Supabase Server Action persistence (the data model above) or guard every localStorage access with 'if (typeof window !== undefined)'. The prompt in this page's V0 path explicitly specifies this guard — check the generated code to confirm it was applied.

## Best practices

- Always proxy the Directions API through a server-side Edge Function or Server Action — exposing a secret key in client code is a billing risk and a potential ban from the mapping provider
- Fetch only the current viewport's route — do not preload alternate routes in the background unless the user explicitly requests them
- Fit the map viewport to the route bounding box automatically after render so users never have to pan to find their route
- Show estimated time and distance in human-readable form (23 min, 4.2 km) above the turn list — users make go/no-go decisions on this information
- Debounce autocomplete keystrokes at 300ms — firing a search request on every character is wasteful and creates visual jitter in the suggestion list
- Encode the full route (origin, destination, waypoints, mode) in the URL query string on every change so the back button and link sharing work without a save action
- Handle the no-route-found error explicitly — certain origin/destination pairs (e.g. islands with no road bridge) return an empty routes array; display a friendly message rather than a blank map or a JavaScript exception
- Cache Directions API responses client-side for the session — if a user toggles between driving and walking mode and then back, the driving route should not require a second API call

## Frequently asked questions

### How do I add route planning to a mobile app with FlutterFlow?

Use flutter_map with a PolylineLayer for the map, and wire an HTTP action to a Supabase Edge Function that calls the Mapbox Directions API. The critical step most FlutterFlow builds miss is using a Custom Action (Dart) to extract the nested geometry.coordinates array from the API response — FlutterFlow's built-in JSON path extractor cannot reliably navigate it. Set the extracted LatLng list into a Page State variable and bind it to your PolylineLayer.

### What is the difference between the Mapbox Directions API and Google Maps Directions API?

Both return route polylines and turn-by-turn steps, but they differ on price and coverage. Mapbox offers 100K free requests per month ($1/1K after); Google gives a $200/mo credit (~40K requests) then charges $5/1K. Google has better transit (bus/subway) routing. Mapbox has more flexible map styling and a simpler GL JS integration. For most driving/walking/cycling apps, Mapbox is the more cost-effective choice at early scale.

### How do I secure my Mapbox API key in a Lovable or V0 app?

Store the secret key in Lovable's Cloud tab Secrets (or V0's Vars panel as a non-NEXT_PUBLIC_ variable) and proxy every Directions API call through a Supabase Edge Function or Next.js Server Action. The frontend only sends waypoints; the server reads the key from environment variables and returns the route. Your public (publishable) map token for tile rendering can go in a NEXT_PUBLIC_ or VITE_ variable — it is safe to expose. The secret Directions API key must never reach the browser.

### Can I add multiple stops to a route planner built with AI tools?

Yes, up to 3–5 waypoints is achievable in a Lovable or V0 build. The prompt in this page's build paths specifies the waypoint array structure explicitly. Beyond 5 waypoints, or when the order of stops needs to be optimised (not just specified), you will need the Mapbox Optimization API and custom server logic — that is outside what a single AI-generated prompt reliably covers.

### How do I draw a route polyline on a Mapbox map?

Call map.addSource() with a GeoJSON source containing the LineString from the Directions API response, then call map.addLayer() with type: 'line' and your paint properties (line-color, line-width). On Flutter with flutter_map, pass the decoded LatLng list to a PolylineLayer widget. In both cases, use map.fitBounds() or flutter_map's fitBounds controller method to auto-zoom the viewport to the full route extent.

### Does route planning work offline?

Not with AI-generated builds. Offline route caching requires downloading map tile packages (MBTiles) and caching the route GeoJSON in local SQLite storage before the user loses connectivity. This is achievable with the Mapbox Offline Downloads SDK in a custom Flutter build, but it adds significant complexity — plan for 1–2 extra weeks of custom development and a more thorough App Store review due to the background data usage.

### How much does the Mapbox Directions API cost?

The first 100,000 requests per month are free. After that, Mapbox charges approximately $1.00 per 1,000 requests. Each route calculation — regardless of the number of waypoints — counts as one request. At 10,000 active users making an average of 10 route requests each per month, you are at 100K requests and just crossing into the paid tier. The Supabase Edge Function that proxies the calls also stays within the free invocation limit at this scale.

### How do I let users save their planned routes?

Insert a row into the saved_routes table (schema in this page's data model section) via a Server Action or Edge Function when the user taps 'Save Route'. Store origin_name, destination_name, the waypoints array as jsonb, mode, and the distance and duration from the Directions API response. Do not store the full GeoJSON — recalculate the route on load from the stored waypoints. RLS ensures each user sees only their own saved routes.

---

Source: https://www.rapidevelopers.com/app-features/dynamic-map-route-planner
© RapidDev — https://www.rapidevelopers.com/app-features/dynamic-map-route-planner
