Manage page layouts
Design
You can apply unique HTML and CSS styling to each Kinde application within the same business account. Although you can only connect one Git repository for your custom pages, you can use conditional rendering to serve different designs based on which application is being accessed.
This guide will walk you through how to use the application’s Client ID to render unique styles and layouts. This doc is based on the tests and implementation we ran and provides example code, folder layout, and implemention checklist.
The clientID is a unique identifier assigned to each application in Kinde. This value distinguishes one app from another, allowing your template code to detect which application is active and render the appropriate HTML and CSS for each app accordingly.
The key to this solution is the clientId
, a unique identifier assigned to every application you create in Kinde. This ID is passed in the request
object to your custom pages. By checking the value of the clientId
, your code can determine which application is active and render the corresponding HTML and CSS.
This process happens server-side, so there’s no performance loss for your users.
Your repository needs to follow a specific structure for Kinde to recognize the files. For this example, we are using the simplest structure with the (default)
folder to act as a fallback and a page.tsx
file within it. This file will contain your conditional logic.
myApp/├── kindeSrc/│ └── environment/│ └── pages/│ └── (kinde)/│ └── (default)/│ └── page.tsx│ └── layout.tsx├── package.json└── kinde.json
For this specific example, we focus on the default design, but you can use this for your sign up and sign pages as well.
In your page.tsx
file, you can access the clientId
from the request
object that is passed to your page component.
const DefaultPage: React.FC<KindePageEvent> = async ({ context, request }) => { let clientId = request.authUrlParams?.clientId || null;
// ... rest of your code};`
Now you can use the clientId
variable to build your conditional logic. For example, you can use an if/else
statement, a switch
statement, or a ternary operator to render different JSX for each application.
The following example shows how to render different content for three separate applications (Next.js, Python, and Nuxt.js), with a default fallback.
kindeSrc/environment/pages/(kinde)/(default)/page.tsx
'use server';
import { getKindeWidget, fetch, type KindePageEvent,} from '@kinde/infrastructure';import React from 'react';import { renderToString } from 'react-dom/server.browser';import Layout from '../../layout';
const DefaultPage: React.FC<KindePageEvent> = async ({ context, request }) => { // 1. Access the client ID from the request object let clientId = request.authUrlParams?.clientId || null; // Environment variables const BUILDER_API_KEY = process.env.BUILDER_API_KEY; const CLIENT_ID_NEXT_APP = process.env.CLIENT_ID_NEXT_APP; const CLIENT_ID_PYTHON_APP = process.env.CLIENT_ID_PYTHON_APP;
// Example: Fetching dynamic content (optional) const res = await fetch( `https://cdn.builder.io/api/v3/content/login-page-data?apiKey=${BUILDER_API_KEY}&sort.createdDate=-1`, { headers: {}, method: 'GET', } ); const { loginPageImage, signInFormTextTop, signupFormTextTop, signInFormTextBottom, signupFormTextBottom, } = res?.data?.results?.[0]?.data || {}; const isUserOnLoginOrRegisterPage = request?.route?.flow;
return ( <Layout context={context} request={request} props={res?.data?.results?.[0]?.data} > {/* 2. Use conditional rendering based on the client ID */} {clientId == CLIENT_ID_NEXT_APP ? (
<div className='container'> {/* Content for the Next.js App */} <h1>Styling for the First App - Next.js</h1> <h2>Client ID: {clientId}</h2> <div className='login-form-wrapper'> {getKindeWidget()} </div> </div>
) : clientId == CLIENT_ID_PYTHON_APP? (
<div className='container-2'> {/* Content for the Python App */} <h1>Styling for the Second App - Python</h1> <h2>Client ID: {clientId}</h2> <table> <tr> <th>Company</th> <th>Contact</th> <th>Country</th> </tr> <tr> <td>Alfreds Futterkiste</td> <td>Maria Anders</td> <td>Germany</td> </tr> </table> <div className='login-form-wrapper'> {getKindeWidget()} </div> </div>
) : (
<div className='container-default'> {/* Fallback content for any other app (e.g., Nuxt app) */} <h1>Default Kinde Authentication Page</h1> <h2>Client ID: {clientId || 'Not available'}</h2> <div className='login-form-wrapper'> {getKindeWidget()} </div> </div>
)} </Layout> );};
// Page Componentexport default async function Page(event: KindePageEvent): Promise<string> { const page = await DefaultPage(event); return renderToString(page);}
The custom CSS for classes like .container
and .container-2
would typically be defined in your layout.tsx
file or in a separate CSS file.
When a user authenticates through each of your applications, they will see a different page design based on your conditional logic. The examples below are not over-designed, but you could make them as distinct as you want.
Next.js App (clientId
: ca3c184xxxxxxd842777bc***xxxx
)
Python App (clientId
: c41376xxxxxx244b3122dd***xxxx
)
Nuxt.js App or any other app (clientId
: 730c34be0xxxxxxe206ec07***xxxx
)