Ludicord
Guide 06 · Build the UI

Navigation

Move between typed embed routes with links, history, params, and prefetching.

Ludicord 3.1.0Public source 20c4889

Ludicord navigation changes the active embed without reloading the Activity shell. Route types are generated from the app/embeds tree, so invalid static paths can fail in the editor before they reach Discord.

Use Link for normal user navigation. It renders an anchor, supports keyboard and browser semantics, and prefetches visible or focused routes by default.

components/navbar.tsxtsx
1import { Link } from "ludicord/navigation";
2
3export default function Navbar() {
4 return (
5 <nav>
6 <Link href="home">Home</Link>
7 <Link href="me">My profile</Link>
8 </nav>
9 );
10}

Set prefetch={false} for large routes that should load only after a click. Set replace when the current screen should not remain in Activity history.

Use useEmbedRouter() when navigation happens after an action.

app/embeds/lobby/embed.tsxtsx
1import { useEmbedRouter } from "ludicord/navigation";
2
3export default function embed() {
4 const router = useEmbedRouter();
5 return <button onClick={() => router.push("game/room-42")}>Join room</button>;
6}
Method or valueBehavior
push(path)Opens a route and adds history
replace(path)Opens a route without keeping the current entry
prefetch(path)Loads the route module before navigation
back()Returns to the previous embed
canGoBackReports whether back() has an Activity history entry

#Read the current path

useEmbedPath() returns the normalized active embed path. It is useful for navigation state, analytics, or sending a current-view signal to a realtime route.

components/audience.tsxtsx
1const path = useEmbedPath();
2
3useEffect(() => {
4 if (connection.status === "open") connection.emit("view", { path });
5}, [connection, connection.status, path]);

#Read dynamic params

Pass the route pattern to useEmbedParams() so generated declarations can infer the parameter names.

app/embeds/game/[roomId]/embed.tsxtsx
1const { roomId } = useEmbedParams("game/:roomId");

Route params are URL input, not authorization. If roomId selects protected server data, the API or WebSocket route must verify that the current session may access it.

#Route groups

Folders wrapped in parentheses organize code but are removed from the public path.

texttext
1app/embeds/(games)/chess/embed.tsx → chess
2app/embeds/(games)/poker/embed.tsx → poker

Use groups to colocate layouts and boundaries for a section without making internal folder names part of the Activity URL.

Embed navigation is internal to the Activity. It does not navigate the Discord client or change the Discord channel. Use SDK commands from useDiscordCommands() only when you intentionally need a Discord capability.

#Next step

Open Styling to make the shell and screens adapt to Discord's desktop and mobile surfaces.