Skip to content

Instantly share code, notes, and snippets.

@mosch
Last active April 27, 2026 14:34
Show Gist options
  • Select an option

  • Save mosch/c2a4df43b85db6754d123273d2bd1593 to your computer and use it in GitHub Desktop.

Select an option

Save mosch/c2a4df43b85db6754d123273d2bd1593 to your computer and use it in GitHub Desktop.

Setting up your Zudoku dev portal with a public landing page

Why this matters

Right now your portal redirects every visitor straight into the auth provider's login screen because there's no public route. That's a rough first impression — visitors land on a screen that says "sign in" without any explanation of what they're signing into. A small structural change fixes that:

  1. Public landing page at / — explains what the portal is, with a "Sign in" CTA.
  2. Everything else protected — docs, API references, etc. are gated behind protectedRoutes.

This way, unauthenticated visitors get a real homepage, and clicking "Sign in" (or any protected link) sends them through your auth provider with a return URL back to the page they wanted. We're also shipping a related fix soon (PR #2398) that removes an extra "login or register?" dialog and goes straight to the auth provider — together with the homepage, this gives a much cleaner flow.

Step 1 — Build a landing page component

Create src/Landingpage.tsx. The cosmo-cargo example has a complete reference you can copy from: examples/cosmo-cargo/src/Landingpage.tsx.

The key trick is using useAuth() so the CTA changes once a user is signed in:

import { Button, Head, Link, Typography } from "zudoku/components";
import { useAuth } from "zudoku/hooks";

export const Landingpage = () => {
  const { isAuthenticated, signIn } = useAuth();

  return (
    <section>
      <Head><title>Home</title></Head>
      <h1>Welcome to the Acme Developer Portal</h1>
      <Typography>
        <p>Browse our APIs, manage keys, and ship integrations fast.</p>
      </Typography>
      {isAuthenticated ? (
        <Button asChild size="xl">
          <Link to="/documentation">Go to docs</Link>
        </Button>
      ) : (
        <Button size="xl" onClick={() => signIn()}>
          Sign in to continue
        </Button>
      )}
    </section>
  );
};

Step 2 — Wire it as the homepage

In zudoku.config.tsx, add a custom-page entry at the top of navigation with path: "/". See examples/cosmo-cargo/zudoku.config.tsx lines 258–263 for the exact pattern:

import { Landingpage } from "./src/Landingpage";

const config: ZudokuConfig = {
  // ...
  navigation: [
    {
      type: "custom-page",
      path: "/",
      element: <Landingpage />,
    },
    // ...the rest of your navigation
  ],
};

Docs reference: Custom Pages guide and type: custom-page in navigation.

Step 3 — Protect everything else

Use protectedRoutes to lock the rest of the site. The simple array form is enough for most cases:

const config: ZudokuConfig = {
  // ...
  protectedRoutes: [
    "/documentation/*",
    "/api/*",
    "/catalog/*",
    // add any other paths you want gated
  ],
};

See the full Protected Routes docs for advanced patterns.

Step 4 — Hide auth-only items from the nav

For pages that should only appear in the sidebar/header for signed-in users, add display: "auth" to the navigation item. Cosmo-cargo uses this for its "Members" and "VIP Lounge" pages — see zudoku.config.tsx lines 334–347:

{
  type: "custom-page",
  path: "/only-members",
  label: "Only members",
  display: "auth",          // hidden until signed in
  element: <MembersOnly />,
},

You can use the same display: "auth" on category, doc, and link items too.

What to copy-paste from cosmo-cargo

If you want a working reference to clone locally and look around in:

git clone https://github.com/zuplo/zudoku.git
cd zudoku
pnpm install
nx run cosmo-cargo:dev

The files most relevant to your setup:

  • examples/cosmo-cargo/src/Landingpage.tsx — public homepage component
  • examples/cosmo-cargo/src/MembersOnly.tsx — example of an auth-only page using useAuth()
  • examples/cosmo-cargo/zudoku.config.tsx — full config showing custom-page at /, protectedRoutes, display: "auth", and authentication together

One note on the dialog you're seeing

You also hit an unintended behavior where Zudoku showed an intermediate "log in or register?" dialog before bouncing to your auth provider. We're removing that dialog in PR #2398 so unauthorized navigation auto-redirects to the provider directly — you'll get this for free on your next Zudoku upgrade. The homepage approach above is the right setup either way and will only get cleaner once that ships.

Let me know once you've stood up the landing page — happy to look over the config and call out anything I'd tweak.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment