docs: Improve README for local development and standardize tab usage (#11865)

## Problem statement

- `<Tabs>` were not rendered in docs
- Missing steps in README for local dev

## How it was solved

- Enhance the README to better describe local development setup and
clean up formatting.
- Standardize the use of tabs across various components for consistency.

## Additional info

To not cause merge conflicts with
https://github.com/zitadel/zitadel/pull/11729 changes in
`/self-hosting/manage/configure/configure.mdx` were skipped
This commit is contained in:
Silvan
2026-03-23 14:39:07 +00:00
committed by GitHub
parent 70adaff793
commit 4bc899d052
9 changed files with 110 additions and 111 deletions
+55 -21
View File
@@ -8,6 +8,12 @@ Ensure you have followed the [root quick start](../../CONTRIBUTING.md#quick-star
### Local Development ### Local Development
Ensure all dependencies are installed:
```bash
pnpm install
```
Start the development server: Start the development server:
```bash ```bash
@@ -34,28 +40,61 @@ Key scripts for documentation workflows:
### Validation ### Validation
* **Code Quality**: Run `pnpm lint` to check for syntax and style issues in JS/TS/MDX files. * **Code Quality**: Run `pnpm nx run @zitadel/docs:lint` to check for syntax and style issues in JS/TS/MDX files.
* **Content Integrity**: Run `pnpm check:links` to validate content structure, including: * **Content Integrity**: Run `pnpm nx run @zitadel/docs:check-links` to validate content structure, including:
* Broken internal links * Broken internal links
* Missing required front-matter (e.g., `title`) * Missing required front-matter (e.g., `title`)
* Image references * Image references
### Troubleshooting
#### Issue: Nx configuration not found or "nx" command not found
If you see errors like:
```bash
NX Cannot find configuration for task @zitadel/zitadel:@zitadel/docs:generate
Pass --verbose to see the stacktrace.
```
or
```
pnpm nx run @zitadel/docs:dev
 ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command "nx" not found
Did you mean "pnpm nx"?
```
**Root cause**: Your dependency cache or `node_modules` is out of sync.
**Solution**: Run these commands from the repository root in order:
1. **Clear node_modules**: `rm -rf node_modules` — removes the installed dependency folder
2. **Clear pnpm cache**: `pnpm cache delete` — clears pnpm's internal package cache
3. **Reinstall dependencies**: `pnpm install` — reinstalls all dependencies fresh
4. **Test the setup**: `pnpm nx run @zitadel/docs:generate` — verifies everything works
This resolves 99% of setup-related issues.
## Contributing ## Contributing
### Build Process ### Build Process
The docs build process automatically handling the following steps via `generate`: The docs build process automatically handles the following steps via `generate`:
1. Downloads required protoc plugins.
2. Generates gRPC documentation from proto files. 1. Downloads required protoc plugins.
3. Generates API documentation from OpenAPI specs. 2. Generates gRPC documentation from proto files.
4. Generates index files for directory structures. 3. Generates API documentation from OpenAPI specs.
4. Generates index files for directory structures.
### Style Guide ### Style Guide
- **Variables**: Use environment variables in code snippets where possible. * **Variables**: Use environment variables in code snippets where possible.
- **Embedded Content**: Use `_filename.mdx` for content embedded in other pages (not indexed individually). * **Embedded Content**: Use `_filename.mdx` for content embedded in other pages (not indexed individually).
- **Code Embedding**: Use the `file` property in code blocks to embed code from the repo. * **Code Embedding**: Use the `file` property in code blocks to embed code from the repo.
- **Voice**: Use active voice and sentence case for titles. * **Voice**: Use active voice and sentence case for titles.
Refer to the [Google Developer Style Guide](https://developers.google.com/style) for general guidelines. Refer to the [Google Developer Style Guide](https://developers.google.com/style) for general guidelines.
@@ -64,9 +103,9 @@ Refer to the [Google Developer Style Guide](https://developers.google.com/style)
All documentation content is located in the `content` directory. Note that the system strictly accepts **only `.mdx` files**. All documentation content is located in the `content` directory. Note that the system strictly accepts **only `.mdx` files**.
To add a new page: To add a new page:
1. Create a `.mdx` file in the appropriate subdirectory of [`content`](./content).
2. Register the new page in the sidebar settings at [`lib/sidebar-data.ts`](./lib/sidebar-data.ts) to make it accessible in the navigation.
1. Create a `.mdx` file in the appropriate subdirectory of [`content`](./content).
2. Register the new page in the sidebar settings at [`lib/sidebar-data.ts`](./lib/sidebar-data.ts) to make it accessible in the navigation.
### Pull Requests ### Pull Requests
@@ -75,9 +114,4 @@ Pass quality checks before submitting:
```bash ```bash
pnpm nx run @zitadel/docs:build pnpm nx run @zitadel/docs:build
pnpm nx run @zitadel/docs:check:links
``` ```
You can also run specific steps individually:
* `pnpm fetch:remote-content`
* `pnpm generate:api-reference`
@@ -63,13 +63,8 @@ Depending on your authorization method you will have to provide additional param
<Tabs <Tabs
groupId="token-auth-methods" groupId="token-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','client_secret_post','none (PKCE)','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
no additional parameters required no additional parameters required
@@ -77,7 +72,7 @@ no additional parameters required
<Tab value="client_secret_post"> <Tab value="client_secret_post">
no additional parameters required no additional parameters required
</Tab> </Tab>
<Tab value="none"> <Tab value="none (PKCE)">
| Parameter | Description | | Parameter | Description |
| --------------------- | ----------------------------------------------------- | | --------------------- | ----------------------------------------------------- |
@@ -192,13 +187,8 @@ Depending on your authorization method you will have to provide additional param
<Tabs <Tabs
groupId="token-auth-methods" groupId="token-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','client_secret_post','none (PKCE)','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
@@ -215,7 +205,7 @@ Send your `client_id` and `client_secret` as parameters in the body:
| client_secret | client_secret of the application | | client_secret | client_secret of the application |
</Tab> </Tab>
<Tab value="none"> <Tab value="none (PKCE)">
Send your `client_id` and `code_verifier` for us to recompute the `code_challenge` of the authorization request. Send your `client_id` and `code_verifier` for us to recompute the `code_challenge` of the authorization request.
@@ -292,13 +282,8 @@ Depending on your authorization method you will have to provide additional param
<Tabs <Tabs
groupId="token-auth-methods" groupId="token-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','client_secret_post','none (PKCE)','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
@@ -315,7 +300,7 @@ Send your `client_id` and `client_secret` as parameters in the body:
| client_secret | client_secret of the application | | client_secret | client_secret of the application |
</Tab> </Tab>
<Tab value="none"> <Tab value="none (PKCE)">
Send your `client_id` as parameter in the body. No authentication is required. Send your `client_id` as parameter in the body. No authentication is required.
@@ -402,13 +387,8 @@ Depending on your authorization method you will have to provide additional param
<Tabs <Tabs
groupId="token-auth-methods" groupId="token-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','client_secret_post','none (PKCE)','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
@@ -426,7 +406,7 @@ Send your `client_id` and `client_secret` as parameters in the body:
| client_secret | client_secret of the application | | client_secret | client_secret of the application |
</Tab> </Tab>
<Tab value="none"> <Tab value="none (PKCE)">
Send your `client_id` as parameter in the body. No authentication is required. Send your `client_id` as parameter in the body. No authentication is required.
@@ -477,12 +457,9 @@ this endpoint will check if the token is not revoked (by client or logout).
Depending on your authorization method you will have to provide additional parameters or headers: Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs <Tabs
groupId="introspect-auth-methods" groupId="introspect-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
@@ -585,14 +562,9 @@ the corresponding `access_token` will be revoked as well.
Depending on your authorization method you will have to provide additional parameters or headers: Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs <Tabs
groupId="token-auth-methods" groupId="token-auth-methods"
defaultValue="client_secret_basic" defaultIndex={0}
values={[ items={['client_secret_basic','client_secret_post','none (PKCE)','private_key_jwt']}
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
> >
<Tab value="client_secret_basic"> <Tab value="client_secret_basic">
@@ -609,7 +581,7 @@ Send your `client_id` and `client_secret` as parameters in the body:
| client_secret | client_secret of the application | | client_secret | client_secret of the application |
</Tab> </Tab>
<Tab value="none"> <Tab value="none (PKCE)">
Send your `client_id` as parameters in the body: Send your `client_id` as parameters in the body:
@@ -132,7 +132,7 @@ You will need to implement a listener that can receive HTTP requests.
For this example, we will use a simple Go HTTP server that will print the received request to standard output. For this example, we will use a simple Go HTTP server that will print the received request to standard output.
As mentioned before, this validation can and should be applied to any target implementation. As mentioned before, this validation can and should be applied to any target implementation.
<Tabs> <Tabs defaultIndex={0} items={['JSON', 'JWT', 'JWE']}>
<Tab value="JSON"> <Tab value="JSON">
<DynamicCodeBlock lang="go" code={JsonGo} /> <DynamicCodeBlock lang="go" code={JsonGo} />
</Tab> </Tab>
@@ -168,8 +168,12 @@ You can also activate the v2 login for your apps, so users are redirected to `/u
**Customizing the new login:** The easiest way to actually customizing it is to fork the https://github.com/zitadel/zitadel repo and use the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to run your login code on Vercel. **Customizing the new login:** The easiest way to actually customizing it is to fork the https://github.com/zitadel/zitadel repo and use the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to run your login code on Vercel.
<Tabs queryString="deployment"> <Tabs
<Tab value="zitadel_cloud" label="Activate Zitadel Cloud V2 Login" default="true"> groupId="deployment"
defaultIndex={0}
items={['Activate Zitadel Cloud V2 Login', 'Vercel Custom Login Deployment']}
>
<Tab value="Activate Zitadel Cloud V2 Login">
Zitadel Cloud provides the V2 login at the fixed path `/ui/v2/login`. Zitadel Cloud provides the V2 login at the fixed path `/ui/v2/login`.
<PreventLockout components={props.components} /> <PreventLockout components={props.components} />
@@ -181,7 +185,7 @@ You can also activate the v2 login for your apps, so users are redirected to `/u
2. Enable the [loginV2 feature](/reference/api/feature/zitadel.feature.v2.FeatureService.SetInstanceFeatures) on the instance. Leave the base URI empty to use the default. If you enable this feature, the login will be used for every application configured in your Zitadel instance. (Example: https://your-zitadel-instance.zitadel.cloud/ui/v2/login) 2. Enable the [loginV2 feature](/reference/api/feature/zitadel.feature.v2.FeatureService.SetInstanceFeatures) on the instance. Leave the base URI empty to use the default. If you enable this feature, the login will be used for every application configured in your Zitadel instance. (Example: https://your-zitadel-instance.zitadel.cloud/ui/v2/login)
</Tab> </Tab>
<Tab value="vercel_custom" label="Vercel Custom Login Deployment"> <Tab value="Vercel Custom Login Deployment">
The simplest way to deploy the new login for yourself is by using the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to deploy the login directly to your Vercel. The simplest way to deploy the new login for yourself is by using the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to deploy the login directly to your Vercel.
@@ -10,20 +10,16 @@ export default function AuthMethods(props) {
<div> <div>
<Tabs <Tabs
groupId="auth-type" groupId="auth-type"
default="pkce" defaultIndex={0}
values={[ items={["PKCE","Basic Auth","JWT with Private Key"]}
{ label: "PKCE", value: "pkce" },
{ label: "Basic Auth", value: "basic" },
{ label: "JWT with Private Key", value: "jwtpk" },
]}
> >
<Tab value="pkce"> <Tab value="PKCE">
<PKCE components={props.components} /> <PKCE components={props.components} />
</Tab> </Tab>
<Tab value="basic"> <Tab value="Basic Auth">
<Basic components={props.components} /> <Basic components={props.components} />
</Tab> </Tab>
<Tab value="jwtpk"> <Tab value="JWT with Private Key">
<JWTPrivateKey components={props.components} /> <JWTPrivateKey components={props.components} />
</Tab> </Tab>
</Tabs> </Tabs>
@@ -31,26 +27,23 @@ export default function AuthMethods(props) {
) : props.selected == "native" ? ( ) : props.selected == "native" ? (
<Tabs <Tabs
groupId="auth-type" groupId="auth-type"
default="pkce" defaultIndex={0}
values={[{ label: "PKCE", value: "pkce" }]} items={["PKCE"]}
> >
<Tab value="pkce"> <Tab value="PKCE">
<PKCE components={props.components} Native /> <PKCE components={props.components} Native />
</Tab> </Tab>
</Tabs> </Tabs>
) : props.selected == "spa" ? ( ) : props.selected == "spa" ? (
<Tabs <Tabs
groupId="auth-type" groupId="auth-type"
default="pkce" defaultIndex={0}
values={[ items={["PKCE","Implicit"]}
{ label: "PKCE", value: "pkce" },
{ label: "Implicit", value: "implicit" },
]}
> >
<Tab value="pkce"> <Tab value="PKCE">
<PKCE components={props.components} /> <PKCE components={props.components} />
</Tab> </Tab>
<Tab value="implicit"> <Tab value="Implicit">
<Implicit components={props.components} /> <Implicit components={props.components} />
</Tab> </Tab>
</Tabs> </Tabs>
@@ -53,14 +53,10 @@ This will start a wizard asking you for an application name and a type.
<Tabs <Tabs
groupId="app-type" groupId="app-type"
default="web" defaultIndex={0}
values={[ items={['Web Application','Native Application','Single Page Application']}
{'label': 'Web Application', 'value': 'web'},
{'label': 'Native Application', 'value': 'native'},
{'label': 'Single Page Application', 'value': 'spa'},
]}
> >
<Tab value="web"> <Tab value="Web Application">
#### Authentication method #### Authentication method
@@ -73,7 +69,7 @@ Please change the authentication method here as well, if you did so in the wizar
<AuthMethods components={props.components} selected="web" /> <AuthMethods components={props.components} selected="web" />
</Tab> </Tab>
<Tab value="native"> <Tab value="Native Application">
#### Authentication method #### Authentication method
@@ -82,7 +78,7 @@ When selecting Native the authentication method always needs to be PKCE.
<AuthMethods components={props.components} selected="native" /> <AuthMethods components={props.components} selected="native" />
</Tab> </Tab>
<Tab value="spa"> <Tab value="Single Page Application">
#### Authentication method #### Authentication method
@@ -5,8 +5,8 @@ If you started with Zitadel before version 3, you might have the "Human User [de
In this case please enable the Feature Flag "Use V2 Api in Management Console for User creation" in the Default Settings. In this case please enable the Feature Flag "Use V2 Api in Management Console for User creation" in the Default Settings.
</Callout> </Callout>
<Tabs> <Tabs groupId="create-user-types" defaultIndex={0} items={['Human User', 'Human User [deprecated]', 'Service Account']}>
<Tab value="human" label="Human User" default> <Tab value="Human User">
<img src="/docs/img/guides/console/invitehuman.png" width="680px" alt="Invite Human" /> <img src="/docs/img/guides/console/invitehuman.png" width="680px" alt="Invite Human" />
When creating a new user you have different options. When creating a new user you have different options.
@@ -21,7 +21,7 @@ In this case please enable the Feature Flag "Use V2 Api in Management Console fo
- **Set an initial password for the user**: The user will receive an email and be able to setup an authentication method (e.g Password, Passkey, External SSO) - **Set an initial password for the user**: The user will receive an email and be able to setup an authentication method (e.g Password, Passkey, External SSO)
</Tab> </Tab>
<Tab value="human_deprecated" label="Human User [deprecated]"> <Tab value="Human User [deprecated]">
<img src="/docs/img/guides/console/addhuman.png" width="680px" alt="Add Human" /> <img src="/docs/img/guides/console/addhuman.png" width="680px" alt="Add Human" />
After a user is created, by default, an initialization mail with a code is sent to the registered email. This code then has to be verified on first login. After a user is created, by default, an initialization mail with a code is sent to the registered email. This code then has to be verified on first login.
@@ -29,7 +29,7 @@ In this case please enable the Feature Flag "Use V2 Api in Management Console fo
If no password is set initially, the initialization mail prompting the user to set his password is sent. If no password is set initially, the initialization mail prompting the user to set his password is sent.
</Tab> </Tab>
<Tab value="service" label="Service Account"> <Tab value="Service Account">
<img <img
src="/docs/img/guides/console/addmachine.png" src="/docs/img/guides/console/addmachine.png"
width="540px" width="540px"
@@ -39,8 +39,8 @@ A provider with HTTP type will send the messages and the data to a pre-defined w
### Configuring a HTTP provider ### Configuring a HTTP provider
<Tabs> <Tabs groupId="http-provider-configuration" defaultIndex={0} items={['SMS', 'Email']}>
<Tab value="sms" label="SMS" default> <Tab value="SMS">
First [add a new SMS Provider of type HTTP](/reference/api/admin/zitadel.admin.v1.AdminService.AddSMSProviderHTTP) to create a new HTTP provider that can be used to send SMS messages: First [add a new SMS Provider of type HTTP](/reference/api/admin/zitadel.admin.v1.AdminService.AddSMSProviderHTTP) to create a new HTTP provider that can be used to send SMS messages:
@@ -74,7 +74,7 @@ A provider with HTTP type will send the messages and the data to a pre-defined w
See full API reference for [SMS Providers](/reference/api/admin) for more details. See full API reference for [SMS Providers](/reference/api/admin) for more details.
</Tab> </Tab>
<Tab value="email" label="Email"> <Tab value="Email">
First [add a new Email Provider of type HTTP](/reference/api/admin/zitadel.admin.v1.AdminService.AddEmailProviderHTTP) to create a new HTTP provider that can be used to send Email messages: First [add a new Email Provider of type HTTP](/reference/api/admin/zitadel.admin.v1.AdminService.AddEmailProviderHTTP) to create a new HTTP provider that can be used to send Email messages:
@@ -169,11 +169,11 @@ Topics of the onboarding term may include
- Migration - Migration
- Security Best Practices & Go-Live Checkup - Security Best Practices & Go-Live Checkup
<Tabs> <Tabs groupId="covered-topics" defaultIndex={1} items={['Topics in scope', 'Topics out of scope']}>
<Tab value="in" label="Topics in scope"> <Tab value="Topics in scope">
<ul> <ul>
<li>Instance Settings</li> <li>Instance Settings</li>
<li>Walk-though all features</li> <li>Walk-through all features</li>
<li>Users / Manuals</li> <li>Users / Manuals</li>
<li>Authentication & Management APIs</li> <li>Authentication & Management APIs</li>
<li>Validation of tokens</li> <li>Validation of tokens</li>
@@ -189,7 +189,7 @@ Topics of the onboarding term may include
<li>Functional testing of the ZITADEL instance</li> <li>Functional testing of the ZITADEL instance</li>
</ul> </ul>
</Tab> </Tab>
<Tab value="out" label="Topics out of scope" default> <Tab value="Topics out of scope">
<ul> <ul>
<li>Performance testing</li> <li>Performance testing</li>
<li>Setting up or maintaining backup storage</li> <li>Setting up or maintaining backup storage</li>