diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0748769b2d..aa8b49e0c3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -745,6 +745,20 @@ The video below shows what this example looks like once rendered.
https://github.com/clerk/clerk-docs/assets/2615508/9b07ba1d-8bb0-498b-935f-432d2d047ab6
+### Tooltips
+
+A tooltip is content that appears when the user hovers over a word or phrase in order to provide additional information. A common use case is for definitions.
+
+Tooltips are defined in the `_tooltips` folder and written in MDX, but they do not support custom MDX components, like callouts or `` components. Try to keep the tooltip content as text.
+
+The tooltip syntax is similar to a link, but with a `!` prefix, as shown in the following example:
+
+```mdx
+The ID of the [active organization](!active-organization) that the user belongs to.
+```
+
+Tooltips should follow the same styleguide as links - only add them on the first mention of a term and only in the highest heading section. So if a term is mentioned in an H2 section and again in its H3 section, it doesn't need to be added in the H3 section.
+
### ``
The `` component is used at the beginning of a tutorial-type content page. It accepts the following properties:
diff --git a/docs/_partials/clerk-middleware-options.mdx b/docs/_partials/clerk-middleware-options.mdx
index bfce13633e..99951a6dad 100644
--- a/docs/_partials/clerk-middleware-options.mdx
+++ b/docs/_partials/clerk-middleware-options.mdx
@@ -46,7 +46,7 @@ The `clerkMiddleware()` function accepts an optional object. The following optio
- `organizationSyncOptions?`
- [OrganizationSyncOptions](#organization-sync-options) | undefined
- Used to activate a specific [organization](/docs/organizations/overview) or [personal account](/docs/organizations/overview#allow-personal-accounts) based on URL path parameters. If there's a mismatch between the active organization in the session (e.g., as reported by [`auth()`](/docs/references/nextjs/auth)) and the organization indicated by the URL, the middleware will attempt to activate the organization specified in the URL.
+ Used to activate a specific [organization](/docs/organizations/overview) or [personal account](/docs/organizations/overview#allow-personal-accounts) based on URL path parameters. If there's a mismatch between the [active organization](!active-organization) in the session (e.g., as reported by [`auth()`](/docs/references/nextjs/auth)) and the organization indicated by the URL, the middleware will attempt to activate the organization specified in the URL.
---
diff --git a/docs/_tooltips/active-organization.mdx b/docs/_tooltips/active-organization.mdx
new file mode 100644
index 0000000000..91aa710261
--- /dev/null
+++ b/docs/_tooltips/active-organization.mdx
@@ -0,0 +1 @@
+A user can be a member of multiple organizations, but only one can be active at a time. The **active organization** determines which organization-specific data the user can access and which role and related permissions they have within the organization.
diff --git a/docs/backend-requests/resources/session-tokens.mdx b/docs/backend-requests/resources/session-tokens.mdx
index 628762a9ba..85baa645b7 100644
--- a/docs/backend-requests/resources/session-tokens.mdx
+++ b/docs/backend-requests/resources/session-tokens.mdx
@@ -76,10 +76,10 @@ Read more about Clerk session tokens and how they work in [the guide on how Cler
| Claim | Abbreviation expanded | Description | Example |
| - | - | - | - |
- | `org_id` | organization ID | The ID of the active organization that the user belongs to. | `org_123` |
- | `org_permissions` | organization permissions | The permissions of the user in the currently active organization. System permissions are not included in the session token. | `["org:admin:example_permission", "org:member:example_permission"]` |
- | `org_slug` | organization slug | The slug of the currently active organization that the user belongs to. | `org-slug` |
- | `org_role` | organization role | The role of the user in the currently active organization. | `org:admin` |
+ | `org_id` | organization ID | The ID of the [active organization](!active-organization) that the user belongs to. | `org_123` |
+ | `org_permissions` | organization permissions | The permissions of the user in the currently [active organization](!active-organization). System permissions are not included in the session token. | `["org:admin:example_permission", "org:member:example_permission"]` |
+ | `org_slug` | organization slug | The slug of the currently [active organization](!active-organization) that the user belongs to. | `org-slug` |
+ | `org_role` | organization role | The role of the user in the currently [active organization](!active-organization). | `org:admin` |
The **`act` (actor) claim** is only included if the user is [impersonating](/docs/users/user-impersonation) another user. It's value is an object that contains the following properties:
diff --git a/docs/components/billing/checkout-button.mdx b/docs/components/billing/checkout-button.mdx
index 4b93fbded9..d759b3059e 100644
--- a/docs/components/billing/checkout-button.mdx
+++ b/docs/components/billing/checkout-button.mdx
@@ -78,7 +78,7 @@ The `` component renders a button that opens the checkout draw
>
```
-`` will throw an error if the `for` prop is set to `'organization'` and no [active organization](/docs/organizations/overview#active-organization) is set.
+`` will throw an error if the `for` prop is set to `'organization'` and no [active organization](!active-organization) is set.
```tsx
<>
diff --git a/docs/components/billing/subscription-details-button.mdx b/docs/components/billing/subscription-details-button.mdx
index 0f32394abc..5e09fdb56d 100644
--- a/docs/components/billing/subscription-details-button.mdx
+++ b/docs/components/billing/subscription-details-button.mdx
@@ -59,7 +59,7 @@ All props are optional.
>
```
-`` will throw an error if the `for` prop is set to `'organization'` and no [active organization](/docs/organizations/overview#active-organization) is set.
+`` will throw an error if the `for` prop is set to `'organization'` and no [active organization](!active-organization) is set.
```tsx
<>
diff --git a/docs/components/organization/organization-switcher.mdx b/docs/components/organization/organization-switcher.mdx
index e06356dc7e..e3b025e55c 100644
--- a/docs/components/organization/organization-switcher.mdx
+++ b/docs/components/organization/organization-switcher.mdx
@@ -23,7 +23,7 @@ The `` component accepts the following properties, all o
- `afterLeaveOrganizationUrl`
- `string`
- The full URL or path to navigate to after the user leaves the currently active organization.
+ The full URL or path to navigate to after the user leaves the currently [active organization](!active-organization).
---
diff --git a/docs/custom-flows/manage-membership-requests.mdx b/docs/custom-flows/manage-membership-requests.mdx
index da464acf76..6442330041 100644
--- a/docs/custom-flows/manage-membership-requests.mdx
+++ b/docs/custom-flows/manage-membership-requests.mdx
@@ -11,7 +11,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for
The following example:
- 1. Uses the [`useOrganization()`](/docs/hooks/use-organization) hook to get `membershipRequests`, which is a list of the active organization's membership requests.
+ 1. Uses the [`useOrganization()`](/docs/hooks/use-organization) hook to get `membershipRequests`, which is a list of the [active organization's](!active-organization) membership requests.
- `membershipRequests` is an object with `data` that contains an array of [`OrganizationMembershipRequest`](/docs/references/javascript/types/organization-membership-request) objects.
- Each `OrganizationMembershipRequest` object has an [`accept()`](/docs/references/javascript/types/organization-membership-request#accept) and [`reject()`](/docs/references/javascript/types/organization-membership-request#reject) method to accept or reject the membership request, respectively.
1. Maps over the `data` array to display the membership requests in a table, providing an "Accept" and "Reject" button for each request that calls the `accept()` and `reject()` methods, respectively.
diff --git a/docs/custom-flows/manage-organization-invitations.mdx b/docs/custom-flows/manage-organization-invitations.mdx
index 135ced01d8..a217801c6c 100644
--- a/docs/custom-flows/manage-organization-invitations.mdx
+++ b/docs/custom-flows/manage-organization-invitations.mdx
@@ -20,12 +20,12 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for
To invite a user:
- 1. Use the [`useOrganization()`](/docs/hooks/use-organization) hook to get `organization`, which is the active organization.
+ 1. Use the [`useOrganization()`](/docs/hooks/use-organization) hook to get `organization`, which is the [active organization](!active-organization).
1. Use `organization` to call the [`inviteMember()`](/docs/references/javascript/organization#invite-member) method, with the recipient's email address and desired role passed as arguments.
To revoke an invitation:
- 1. Use the `useOrganization()` hook to get `invitations`, which is a list of invitations for the active organization.
+ 1. Use the `useOrganization()` hook to get `invitations`, which is a list of invitations for the [active organization](!active-organization).
1. `invitations` is an array of [`OrganizationInvitation`](/docs/references/javascript/types/organization-invitation) objects. Each `OrganizationInvitation` object has a [`revoke()`](/docs/references/javascript/types/organization-invitation#revoke) method that can be called to revoke the invitation.
The following example includes:
@@ -218,7 +218,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for
To check if the current user is an organization admin:
- 1. Get the active organization's ID from the `clerk` object.
+ 1. Get the [active organization's](!active-organization) ID from the `clerk` object.
1. Call the [`getOrganizationMemberships()`](/docs/references/javascript/user#get-organization-memberships) method to get a list of organizations that the user is a member of. This method returns `data`, which is an array of `OrganizationMembership` objects.
1. In the list of organizations that the user is a member of, find the `OrganizationMembership` object that has an ID that matches the active organization's ID.
1. Check the `role` property of the `OrganizationMembership` object to see if the user is an admin.
diff --git a/docs/custom-flows/manage-roles.mdx b/docs/custom-flows/manage-roles.mdx
index 789c07bdf2..1dd61bf035 100644
--- a/docs/custom-flows/manage-roles.mdx
+++ b/docs/custom-flows/manage-roles.mdx
@@ -13,7 +13,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for
The following example:
- 1. Uses the [`useOrganization()`](/docs/hooks/use-organization) hook to get `memberships`, which is a list of the active organization's memberships.
+ 1. Uses the [`useOrganization()`](/docs/hooks/use-organization) hook to get `memberships`, which is a list of the [active organization's](!active-organization) memberships.
- `memberships` is an object with `data` that contains an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects.
- Each `OrganizationMembership` object has an [`update()`](/docs/references/javascript/types/organization-membership#update) and [`destroy()`](/docs/references/javascript/types/organization-membership#destroy) method to update the member's role and remove the member from the organization, respectively.
1. Maps over the `data` array to display the memberships in a table, providing an "Update Role" and "Remove Member" button for each membership that calls the `update()` and `destroy()` methods, respectively.
diff --git a/docs/custom-flows/organization-switcher.mdx b/docs/custom-flows/organization-switcher.mdx
index 11c358d6c0..3785cff13e 100644
--- a/docs/custom-flows/organization-switcher.mdx
+++ b/docs/custom-flows/organization-switcher.mdx
@@ -14,7 +14,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for
The following examples:
1. Use the [`useOrganizationList()`](/docs/hooks/use-organization-list) hook to get `memberships`, which is a list of the current user's organization memberships. `memberships` returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects.
- 1. Map over the `data` array to display the user's organization memberships in a table, providing a button that calls `setActive()` to set the selected organization as the active organization.
+ 1. Map over the `data` array to display the user's organization memberships in a table, providing a button that calls `setActive()` to set the selected organization as the [active organization](!active-organization).
- If there are no organizations, the [`` component (custom-flow version, not the Clerk component)](/docs/custom-flows/create-organizations) is rendered to allow the user to create an organization.
The difference between the two examples is the parameters passed to the `useOrganizationList()` hook in order to determine how the list is paginated.
diff --git a/docs/custom-flows/update-organizations.mdx b/docs/custom-flows/update-organizations.mdx
index d72efaaf84..902bd1467e 100644
--- a/docs/custom-flows/update-organizations.mdx
+++ b/docs/custom-flows/update-organizations.mdx
@@ -68,7 +68,7 @@ This guide will demonstrate how to use Clerk's API to build a custom flow for up
- The following example uses the `organization.update()` method to update the active organization's name. To see what other attributes can be updated, see the [`update()` reference doc](/docs/references/javascript/organization#update).
+ The following example uses the `organization.update()` method to update the [active organization's](!active-organization) name. To see what other attributes can be updated, see the [`update()` reference doc](/docs/references/javascript/organization#update).
Use the tabs to view the code necessary for the `index.html` and `main.js` files.
diff --git a/docs/guides/authorization-checks.mdx b/docs/guides/authorization-checks.mdx
index 0c3060d09f..7d2227d49d 100644
--- a/docs/guides/authorization-checks.mdx
+++ b/docs/guides/authorization-checks.mdx
@@ -39,7 +39,7 @@ This guide will show you how to implement authorization checks in order to prote
- When doing authorization checks, it's recommended to use permission-based over role-based, and feature-based over plan-based authorization, as these approaches are more granular, flexible, and more secure.
- Note: Using `has()` **on the server-side** to check permissions works only with **custom permissions**, as [system permissions](/docs/organizations/roles-permissions#system-permissions) aren't included in the session token claims. To check system permissions, verify the user's role instead.
-- Checking for a role or permission depends on the user having an [active organization](/docs/organizations/overview#active-organization). Without an active organization, the authorization checks will likely always evaluate to false by default.
+- Checking for a role or permission depends on the user having an [active organization](!active-organization). Without an active organization, the authorization checks will likely always evaluate to false by default.
- If you would like to perform role-based authorization checks **without** using Clerk's organizations feature, see [the Role Based Access Control (RBAC) guide](/docs/references/nextjs/basic-rbac).
- Permission-based authorization checks link with feature-based authorization checks. This means that if you are checking a custom permission, it will only work if the feature part of the permission key (`org::`) **is a feature included in the organization's active plan**. For example, say you want to check if an organization member has the custom permission `org:teams:manage`, where `teams` is the feature. Before performing the authorization check, you need to ensure that the user's organization is subscribed to a plan that has the `teams` feature. If the user's organization is not subscribed to a plan that has the `teams` feature, the authorization check will always return `false`, even if the user has the custom permission.
diff --git a/docs/guides/multi-tenant-architecture.mdx b/docs/guides/multi-tenant-architecture.mdx
index 6b604e09d2..e4384217f1 100644
--- a/docs/guides/multi-tenant-architecture.mdx
+++ b/docs/guides/multi-tenant-architecture.mdx
@@ -55,10 +55,10 @@ B2B SaaS applications with the following characteristics are well-supported with
Clerk offers a number of building blocks to help integrate organizations into your application:
- The [`` component](/docs/components/organization/organization-switcher) provides a way for your users to select which organization is active. The [`useOrganizationList()` hook](/docs/custom-flows/organization-switcher) can be used for more control.
-- The [`useOrganization()` hook](/docs/hooks/use-organization) can be used to fetch the current, active organization.
+- The [`useOrganization()` hook](/docs/hooks/use-organization) can be used to fetch the current [active organization](!active-organization).
- The [`` component](/docs/components/protect) enables you to limit who can view certain pages based on their role. Additionally, Clerk exposes a number of helper functions, such as [`auth()`](/docs/references/nextjs/auth), and hooks, such as [`useAuth()`](/docs/hooks/use-auth), to check the user's authorization throughout your app and API endpoints.
-The organization's ID should be stored in your database alongside each resource so that it can be used to filter and query the resources that should be rendered or returned according to the active organization.
+The organization's ID should be stored in your database alongside each resource so that it can be used to filter and query the resources that should be rendered or returned according to the [active organization](!active-organization).
## Platforms
diff --git a/docs/hooks/use-organization-list.mdx b/docs/hooks/use-organization-list.mdx
index 15e26acd26..d5d24cb499 100644
--- a/docs/hooks/use-organization-list.mdx
+++ b/docs/hooks/use-organization-list.mdx
@@ -4,7 +4,7 @@ description: Access and manage the current user's organization list in your Reac
sdk: chrome-extension, expo, nextjs, react, react-router, remix, tanstack-react-start
---
-The `useOrganizationList()` hook provides access to the current user's organization memberships, invitations, and suggestions. It also includes methods for creating new organizations and managing the active organization.
+The `useOrganizationList()` hook provides access to the current user's organization memberships, invitations, and suggestions. It also includes methods for creating new organizations and managing the [active organization](!active-organization).
## Parameters
diff --git a/docs/hooks/use-organization.mdx b/docs/hooks/use-organization.mdx
index 4bf92725ed..f315877e46 100644
--- a/docs/hooks/use-organization.mdx
+++ b/docs/hooks/use-organization.mdx
@@ -6,7 +6,7 @@ search:
sdk: chrome-extension, expo, nextjs, react, react-router, remix, tanstack-react-start
---
-The `useOrganization()` hook retrieves attributes of the currently active organization.
+The `useOrganization()` hook retrieves attributes of the currently [active organization](!active-organization).
## Parameters
diff --git a/docs/organizations/org-slugs-in-urls.mdx b/docs/organizations/org-slugs-in-urls.mdx
index ba8362def4..f1322ce15f 100644
--- a/docs/organizations/org-slugs-in-urls.mdx
+++ b/docs/organizations/org-slugs-in-urls.mdx
@@ -106,7 +106,7 @@ This guide shows you how to add organization slugs to your app's URLs, configure
## Configure `clerkMiddleware()` to set the active organization
> [!TIP]
- > If your app doesn't use `clerkMiddleware()`, or you prefer to manually set the active organization, use the [`setActive()`](/docs/references/javascript/clerk) method to control the active organization on the client side.
+ > If your app doesn't use `clerkMiddleware()`, or you prefer to manually set the [active organization](!active-organization), use the [`setActive()`](/docs/references/javascript/clerk) method to control the active organization on the client side.
With [`clerkMiddleware()`](/docs/references/nextjs/clerk-middleware), you can use the [`organizationSyncOptions`](/docs/references/nextjs/clerk-middleware#organization-sync-options) property to declare URL patterns that determine whether a specific organization should be activated.
diff --git a/docs/references/astro/auth-store.mdx b/docs/references/astro/auth-store.mdx
index 69e17f33d9..0169c79424 100644
--- a/docs/references/astro/auth-store.mdx
+++ b/docs/references/astro/auth-store.mdx
@@ -26,21 +26,21 @@ The `$authStore` store provides a convenient way to access the current auth stat
- `orgId`
- `string`
- The ID of the user's active organization.
+ The ID of the user's [active organization](!active-organization).
---
- `orgRole`
- `string`
- The current user's role in their active organization.
+ The current user's role in their [active organization](!active-organization).
---
- `orgSlug`
- `string`
- The URL-friendly identifier of the user's active organization.
+ The URL-friendly identifier of the user's [active organization](!active-organization).
## How to use the `$authStore` store
diff --git a/docs/references/astro/locals.mdx b/docs/references/astro/locals.mdx
index 6710dc79d8..f1b6a3e696 100644
--- a/docs/references/astro/locals.mdx
+++ b/docs/references/astro/locals.mdx
@@ -8,7 +8,7 @@ Through Astro [`locals`](https://docs.astro.build/en/guides/middleware/#storing-
## `locals.auth()`
-`Astro.locals.auth()` returns an `Auth` object. This JavaScript object contains important information like session data, your user's ID, as well as their active organization ID. Learn more about the `Auth` object [here](/docs/references/backend/types/auth-object){{ target: '_blank' }}.
+`Astro.locals.auth()` returns an `Auth` object. This JavaScript object contains important information like session data, your user's ID, as well as the ID of the [active organization](!active-organization). Learn more about the `Auth` object [here](/docs/references/backend/types/auth-object){{ target: '_blank' }}.
### `locals.auth()` options
diff --git a/docs/references/astro/organization-store.mdx b/docs/references/astro/organization-store.mdx
index 75ea58cb56..9dc4b91d08 100644
--- a/docs/references/astro/organization-store.mdx
+++ b/docs/references/astro/organization-store.mdx
@@ -8,7 +8,7 @@ The `$organizationStore` store is used to retrieve attributes of the currently a
## How to use the `$organizationStore` store
-The following example demonstrates how to use the `$organizationStore` store to access the [`Organization`](/docs/references/javascript/organization){{ target: '_blank' }} object, which allows you to access the current active organization.
+The following example demonstrates how to use the `$organizationStore` store to access the [`Organization`](/docs/references/javascript/organization){{ target: '_blank' }} object, which allows you to access the current [active organization](!active-organization).
```tsx {{ filename: 'organization.tsx' }}
diff --git a/docs/references/backend/types/auth-object.mdx b/docs/references/backend/types/auth-object.mdx
index b5c15379d0..548c939587 100644
--- a/docs/references/backend/types/auth-object.mdx
+++ b/docs/references/backend/types/auth-object.mdx
@@ -36,28 +36,28 @@ The `Auth` object contains important information like the current user's session
- `orgId`
- `string | undefined`
- The ID of the user's active organization.
+ The ID of the user's [active organization](!active-organization).
---
- `orgRole`
- [OrganizationCustomRoleKey](/docs/references/javascript/types/organization-custom-role-key) | undefined
- The current user's role in their active organization.
+ The current user's role in their [active organization](!active-organization).
---
- `orgSlug`
- `string | undefined`
- The URL-friendly identifier of the user's active organization.
+ The URL-friendly identifier of the user's [active organization](!active-organization).
---
- `orgPermissions`
- [OrganizationCustomPermissionKey](/docs/references/javascript/types/organization-custom-permission-key)\[] | undefined
- The current user's active organization permissions.
+ The current user's [active organization](!active-organization) permissions.
---
@@ -342,7 +342,7 @@ The `Auth` object is not available in the frontend. To use the `getToken()` meth
## `Auth` object example without active organization
-The following is an example of the `Auth` object without an active organization. Notice that there is no `o` claim. Read more about token claims in the [guide on session tokens](/docs/backend-requests/resources/session-tokens).
+The following is an example of the `Auth` object without an [active organization](!active-organization). Notice that there is no `o` claim. Read more about token claims in the [guide on session tokens](/docs/backend-requests/resources/session-tokens).
@@ -398,7 +398,7 @@ The following is an example of the `Auth` object without an active organization.
## `Auth` object example with active organization
-The following is an example of the `Auth` object with an active organization. Notice the addition of the `o` claim. Read more about token claims in the [guide on session tokens](/docs/backend-requests/resources/session-tokens).
+The following is an example of the `Auth` object with an [active organization](!active-organization). Notice the addition of the `o` claim. Read more about token claims in the [guide on session tokens](/docs/backend-requests/resources/session-tokens).
diff --git a/docs/references/javascript/clerk.mdx b/docs/references/javascript/clerk.mdx
index fb68c6293e..3095541af2 100644
--- a/docs/references/javascript/clerk.mdx
+++ b/docs/references/javascript/clerk.mdx
@@ -575,7 +575,7 @@ function getOrganization(organizationId: string): Promise [!WARNING]
> You must have [**Verified domains**][verified-domains-ref] enabled in your app's settings in the Clerk Dashboard.
@@ -198,7 +198,7 @@ await clerk.organization.getDomain({ domainId: 'domain_123' })
### `getDomains()`
-Retrieves the list of domains for the currently active organization. Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationDomain`][org-domain-ref] objects.
+Retrieves the list of domains for the currently [active organization](!active-organization). Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationDomain`][org-domain-ref] objects.
> [!WARNING]
> You must have [**Verified domains**][verified-domains-ref] enabled in your app's settings in the Clerk Dashboard.
@@ -238,7 +238,7 @@ await clerk.organization.getDomains()
### `getInvitations()`
-Retrieves the list of invitations for the currently active organization. Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationInvitation`][org-inv-ref] objects.
+Retrieves the list of invitations for the currently [active organization](!active-organization). Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationInvitation`][org-inv-ref] objects.
```typescript
function getInvitations(
@@ -277,7 +277,7 @@ await clerk.organization.getInvitations()
### `getMemberships()`
-Retrieves the list of memberships for the currently active organization. Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationMembership`][org-mem-ref] objects.
+Retrieves the list of memberships for the currently [active organization](!active-organization). Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationMembership`][org-mem-ref] objects.
```typescript
function getMemberships(
@@ -314,7 +314,7 @@ For an example on how to use `getMemberships()`, see the [custom flow on managin
### `getMembershipRequests()`
-Retrieve the list of membership requests for the currently active organization. Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationMembershipRequest`][org-mem-ref]-request) objects.
+Retrieve the list of membership requests for the currently [active organization](!active-organization). Returns a [`ClerkPaginatedResponse`][pag-ref] of [`OrganizationMembershipRequest`][org-mem-ref]-request) objects.
> [!WARNING]
> You must have [**Organizations**](/docs/organizations/overview#enable-organizations-in-your-application), and [**Verified domains** and **Automatic suggestion**][verified-domains-ref] enabled in your app's settings in the Clerk Dashboard.
diff --git a/docs/references/javascript/overview.mdx b/docs/references/javascript/overview.mdx
index d60a5bb30a..b9eadc85ae 100644
--- a/docs/references/javascript/overview.mdx
+++ b/docs/references/javascript/overview.mdx
@@ -38,4 +38,4 @@ The [`SignUp`](/docs/references/javascript/sign-up) object holds the state of th
### `Organization`
-Organizations are a flexible and scalable way to manage users and their access to resources within your Clerk application. With organizations, you can assign specific roles and permissions to users, making them useful for managing projects, coordinating teams, or facilitating partnerships. Users can belong to many organizations. One of them will be the ["active organization"](/docs/organizations/overview#active-organization) of the session. It is represented by the [`Organization`](/docs/references/javascript/organization) object. To learn about organizations, see the [dedicated guide](/docs/organizations/overview).
+Organizations are a flexible and scalable way to manage users and their access to resources within your Clerk application. With organizations, you can assign specific roles and permissions to users, making them useful for managing projects, coordinating teams, or facilitating partnerships. Users can belong to many organizations. One of them will be the [active organization](!active-organization) of the session. It is represented by the [`Organization`](/docs/references/javascript/organization) object. To learn about organizations, see the [dedicated guide](/docs/organizations/overview).
diff --git a/docs/references/javascript/session.mdx b/docs/references/javascript/session.mdx
index f609b84f37..1bcb4e3930 100644
--- a/docs/references/javascript/session.mdx
+++ b/docs/references/javascript/session.mdx
@@ -93,7 +93,7 @@ All sessions that are **expired**, **removed**, **replaced**, **ended** or **aba
- `lastActiveOrganizationId`
- `string | null`
- The ID of the last active organization.
+ The ID of the last [active organization](!active-organization).
---
@@ -329,7 +329,7 @@ function getToken(options?: GetTokenOptions): Promise
- `organizationId?`
- `string`
- The organization associated with the generated session token. _Does not modify the session's currently active organization._
+ The organization associated with the generated session token. _Does not modify the session's currently [active organization](!active-organization)._
#### Example
diff --git a/docs/references/javascript/types/set-active-params.mdx b/docs/references/javascript/types/set-active-params.mdx
index 39c5618313..ca0b4f1183 100644
--- a/docs/references/javascript/types/set-active-params.mdx
+++ b/docs/references/javascript/types/set-active-params.mdx
@@ -17,7 +17,7 @@ The parameters for the `setActive()` method.
- `organization`
- [Organization](/docs/references/javascript/organization) | string | null
- The organization resource or organization ID/slug (string version) to be set as active in the current session. If `null`, the currently active organization is removed as active.
+ The organization resource or organization ID/slug (string version) to be set as active in the current session. If `null`, the currently [active organization](!active-organization) is removed as active.
---
diff --git a/docs/references/nextjs/clerk-middleware.mdx b/docs/references/nextjs/clerk-middleware.mdx
index 3998431417..0b6e06c105 100644
--- a/docs/references/nextjs/clerk-middleware.mdx
+++ b/docs/references/nextjs/clerk-middleware.mdx
@@ -417,7 +417,7 @@ object has the type `OrganizationSyncOptions`, which has the following propertie
Patterns must have a path parameter named either `:id` (to match a Clerk organization ID) or `:slug` (to match a Clerk organization slug).
> [!WARNING]
- > If the organization can't be activated—either because it doesn't exist or the user lacks access—the previously active organization will remain unchanged. Components must detect this case and provide an appropriate error and/or resolution pathway, such as calling `notFound()` or displaying an [``](/docs/components/organization/organization-switcher).
+ > If the organization can't be activated—either because it doesn't exist or the user lacks access—the previously [active organization](!active-organization) will remain unchanged. Components must detect this case and provide an appropriate error and/or resolution pathway, such as calling `notFound()` or displaying an [``](/docs/components/organization/organization-switcher).
Common examples:
diff --git a/docs/references/sdk/terminology.mdx b/docs/references/sdk/terminology.mdx
index 8ddee067eb..2dfc09297c 100644
--- a/docs/references/sdk/terminology.mdx
+++ b/docs/references/sdk/terminology.mdx
@@ -11,7 +11,7 @@ A consistent terminology should be used across all user interactions with Clerk'
| Client | A [client](/docs/references/javascript/client){{ target: '_blank' }} represents the current device or software accessing an application such as your web browser, native application, Chrome Extension, or Electron app. |
| Session | A [session](/docs/references/javascript/session){{ target: '_blank' }} is a secure representation of the authentication state of the current user. Each client can hold multiple sessions on the same device. This is identical to how Gmail works in a browser. |
| User | A user represents the current user of the session. The [`User`](/docs/references/javascript/user){{ target: '_blank' }} object holds all the basic user information e.g. name, email addresses, phone numbers, etc… including their public, private, and unsafe metadata. |
-| Organization | An [organization](/docs/references/javascript/organization){{ target: '_blank' }} represents the current organization of the session. Users can belong to many organizations. One of them will be the [active organization](/docs/organizations/overview#active-organization) of the session. |
+| Organization | An [organization](/docs/references/javascript/organization){{ target: '_blank' }} represents the current organization of the session. Users can belong to many organizations. One of them will be the [active organization](!active-organization) of the session. |
| FAPI | [Frontend API of Clerk](/docs/reference/frontend-api){{ target: '_blank' }}. Example: `https://random-name.clerk.accounts.dev` (Production example: `https://clerk.yourdomain.com`). FAPI is the primary API for Clerk’s UI components. Every Clerk development/production instance has a dedicated FAPI. This is the authentication, session, user & organization management API you or your users will interact with. |
| BAPI | [Backend API of Clerk](/docs/reference/backend-api){{ target: '_blank' }}. Currently set to `https://api.clerk.com`. A restful CRUD API for the server-side. |
| Secret Key | Your app’s Secret Key for use in the backend. Do not expose this on the frontend with a public environment variable. Allows for CRUD operations. |
diff --git a/docs/references/vue/use-auth.mdx b/docs/references/vue/use-auth.mdx
index b1eb3e018a..c10de438e3 100644
--- a/docs/references/vue/use-auth.mdx
+++ b/docs/references/vue/use-auth.mdx
@@ -40,21 +40,21 @@ The `useAuth()` composable provides access to the current user's authentication
- `orgId`
- `Ref`
- The ID of the user's active organization.
+ The ID of the user's [active organization](!active-organization).
---
- `orgRole`
- `Ref`
- The current user's role in their active organization.
+ The current user's role in their [active organization](!active-organization).
---
- `orgSlug`
- `Ref`
- The URL-friendly identifier of the user's active organization.
+ The URL-friendly identifier of the user's [active organization](!active-organization).
---
diff --git a/docs/references/vue/use-organization.mdx b/docs/references/vue/use-organization.mdx
index e8513b2763..53304c8926 100644
--- a/docs/references/vue/use-organization.mdx
+++ b/docs/references/vue/use-organization.mdx
@@ -4,7 +4,7 @@ description: Access and manage the currently active organization in your Vue app
sdk: vue
---
-The `useOrganization()` composable retrieves attributes of the currently active organization.
+The `useOrganization()` composable retrieves attributes of the currently [active organization](!active-organization).
## Returns
@@ -19,7 +19,7 @@ The `useOrganization()` composable retrieves attributes of the currently active
- `organization`
- Ref\<[Organization](/docs/references/javascript/organization)>
- The currently active organization.
+ The currently [active organization](!active-organization).
---
diff --git a/docs/upgrade-guides/core-2/chrome-extension.mdx b/docs/upgrade-guides/core-2/chrome-extension.mdx
index ebdab12588..add6c58b36 100644
--- a/docs/upgrade-guides/core-2/chrome-extension.mdx
+++ b/docs/upgrade-guides/core-2/chrome-extension.mdx
@@ -512,7 +512,7 @@ As part of this major version, a number of previously deprecated props, argument
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -525,7 +525,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/docs/upgrade-guides/core-2/expo.mdx b/docs/upgrade-guides/core-2/expo.mdx
index 357079f200..e1a3e70201 100644
--- a/docs/upgrade-guides/core-2/expo.mdx
+++ b/docs/upgrade-guides/core-2/expo.mdx
@@ -504,7 +504,7 @@ As part of this major version, a number of previously deprecated props, argument
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -517,7 +517,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/docs/upgrade-guides/core-2/javascript.mdx b/docs/upgrade-guides/core-2/javascript.mdx
index 0798bc3967..2d3e430748 100644
--- a/docs/upgrade-guides/core-2/javascript.mdx
+++ b/docs/upgrade-guides/core-2/javascript.mdx
@@ -563,7 +563,7 @@ As part of this major version, a number of previously deprecated props, argument
titles={["setSession
-> setActive
", "Organization.create('x')
-> Organization.create({ name: 'x' })
", "Organization.getPendingInvitations()
-> Organization.getInvitations({ status: 'pending' })
", "MagicLinkError
-> EmailLinkError
", "isMagicLinkError
-> isEmailLinkError
", "MagicLinkErrorCode
-> EmailLinkErrorCode
", "useMagicLink
-> useEmailLink
", "handleMagicLinkVerification
-> handleEmailLinkVerification
", "[User|OrganizationMembershipPublicData].profileImageUrl
-> [User|OrganizationMembershipPublicData].imageUrl
", "Clerk.getOrganizationMemberships()
-> user.getOrganizationMemberships()
", "lastOrganizationInvitation
and lastOrganizationMember
dropped from event emitter", "Clerk.redirectToHome()
removed", "Clerk.isReady()
removed", "Replace signOutCallback
prop on SignOutButton
with redirectUrl
", "Clerk
import changed"]}
>
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -576,7 +576,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/docs/upgrade-guides/core-2/nextjs.mdx b/docs/upgrade-guides/core-2/nextjs.mdx
index 8a67ee240c..443154a712 100644
--- a/docs/upgrade-guides/core-2/nextjs.mdx
+++ b/docs/upgrade-guides/core-2/nextjs.mdx
@@ -1045,7 +1045,7 @@ As part of this major version, a number of previously deprecated props, argument
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -1058,7 +1058,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/docs/upgrade-guides/core-2/react.mdx b/docs/upgrade-guides/core-2/react.mdx
index 33891d8d89..344f5a6bd6 100644
--- a/docs/upgrade-guides/core-2/react.mdx
+++ b/docs/upgrade-guides/core-2/react.mdx
@@ -783,7 +783,7 @@ As part of this major version, a number of previously deprecated props, argument
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -796,7 +796,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/docs/upgrade-guides/core-2/remix.mdx b/docs/upgrade-guides/core-2/remix.mdx
index 3c1d7d62cc..af42d51dbb 100644
--- a/docs/upgrade-guides/core-2/remix.mdx
+++ b/docs/upgrade-guides/core-2/remix.mdx
@@ -340,7 +340,7 @@ As part of this major version, a number of previously deprecated props, argument
titles={["setSession
-> setActive
", "Organization.create('x')
-> Organization.create({ name: 'x' })
", "Organization.getPendingInvitations()
-> Organization.getInvitations({ status: 'pending' })
", "isMagicLinkError
-> isEmailLinkError
", "MagicLinkErrorCode
-> EmailLinkErrorCode
", "useMagicLink
-> useEmailLink
", "Replace signOutCallback
prop on SignOutButton
with redirectUrl
"]}
>
- `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently active organization. The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
+ `setSession` should be replaced with `setActive`. The format of the parameters has changed slightly - `setActive` takes an object where `setSession` took params directly. The `setActive` function also can accept an `organization` param that is used to set the currently [active organization](!active-organization). The return signature did not change. Read the [API documentation](/docs/references/javascript/clerk#set-active) for more detail. This function should be expected to be returned from one of the following Clerk hooks: `useSessionList`, `useSignUp`, or `useSignIn`. Some migration examples:
```js {{ prettier: false, del: [1, 4, 7], ins: [2, 5, 8] }}
await setSession('sessionID', () => void)
@@ -353,7 +353,7 @@ As part of this major version, a number of previously deprecated props, argument
await setActive({ session: sessionObj, beforeEmit: () => void })
```
- `setActive` also supports setting an active organization:
+ `setActive` also supports setting an [active organization](!active-organization):
```js {{ prettier: false }}
await setActive({
diff --git a/scripts/build-docs.test.ts b/scripts/build-docs.test.ts
index d7d4487bd9..664c6b0f9b 100644
--- a/scripts/build-docs.test.ts
+++ b/scripts/build-docs.test.ts
@@ -5449,3 +5449,93 @@ Updated Documentation specific to React.js
`)
})
})
+
+describe('Test tooltips', () => {
+ test('Should embed tooltips into a doc', async () => {
+ const { tempDir, readFile } = await createTempFiles([
+ {
+ path: './docs/manifest.json',
+ content: JSON.stringify({
+ navigation: [[{ title: 'API Doc', href: '/docs/api-doc' }]],
+ }),
+ },
+ {
+ path: './docs/api-doc.mdx',
+ content: `---
+title: API Documentation
+description: x
+---
+
+[Tooltip](!ABC)
+`,
+ },
+ {
+ path: './docs/_tooltips/ABC.mdx',
+ content: `React.js is a framework or a library idk`,
+ },
+ ])
+
+ await build(
+ await createConfig({
+ ...baseConfig,
+ basePath: tempDir,
+ validSdks: ['react'],
+ tooltips: {
+ inputPath: '../docs/_tooltips',
+ outputPath: './_tooltips',
+ },
+ }),
+ )
+
+ expect(await readFile('./dist/api-doc.mdx')).toBe(`---
+title: API Documentation
+description: x
+sdkScoped: "false"
+canonical: /docs/api-doc
+---
+
+TooltipReact.js is a framework or a library idk
+`)
+ })
+
+ test('Should validate links in tooltips', async () => {
+ const { tempDir, readFile } = await createTempFiles([
+ {
+ path: './docs/manifest.json',
+ content: JSON.stringify({
+ navigation: [[{ title: 'API Doc', href: '/docs/api-doc' }]],
+ }),
+ },
+ {
+ path: './docs/api-doc.mdx',
+ content: `---
+title: API Documentation
+description: x
+---
+
+[Tooltip](!ABC)
+`,
+ },
+ {
+ path: './docs/_tooltips/ABC.mdx',
+ content: `This is an invalid link [Invalid Link](/docs/invalid-link)`,
+ },
+ ])
+
+ const output = await build(
+ await createConfig({
+ ...baseConfig,
+ basePath: tempDir,
+ validSdks: ['react'],
+ tooltips: {
+ inputPath: '../docs/_tooltips',
+ outputPath: './_tooltips',
+ },
+ }),
+ )
+
+ expect(output).toContain(
+ 'warning Matching file not found for path: /docs/invalid-link. Expected file to exist at /docs/invalid-link.mdx',
+ )
+ })
+})
diff --git a/scripts/build-docs.ts b/scripts/build-docs.ts
index 56d5b11374..c5025ec7f0 100644
--- a/scripts/build-docs.ts
+++ b/scripts/build-docs.ts
@@ -94,8 +94,9 @@ import {
writeRedirects,
type Redirect,
} from './lib/redirects'
+import { checkTooltips } from './lib/plugins/checkTooltips'
+import { readTooltipsFolder, readTooltipsMarkdown } from './lib/tooltips'
import { Flags, readSiteFlags, writeSiteFlags } from './lib/siteFlags'
-import { readTooltipsFolder, readTooltipsMarkdown, writeTooltips } from './lib/tooltips'
import { removeMdxSuffix } from './lib/utils/removeMdxSuffix'
// Only invokes the main function if we run the script directly eg npm run build, bun run ./scripts/build-docs.ts
@@ -242,7 +243,6 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
const getCommitDate = getLastCommitDate(config)
const markDirty = markDocumentDirty(store)
const scopeHref = scopeHrefToSDK(config)
- const writeTooltipsToDist = writeTooltips(config, store)
abortSignal?.throwIfAborted()
@@ -355,7 +355,7 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
}
const markdownFile = await markdownCache(file.filePath, () =>
- parseMarkdownFile(file, partials, typedocs, prompts, inManifest, 'docs'),
+ parseMarkdownFile(file, partials, tooltips, typedocs, prompts, inManifest, 'docs'),
)
if (sdkMatch) {
@@ -374,7 +374,7 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
const inManifest = docsInManifest.has(file.href)
const markdownFile = await markdownCache(file.filePath, () =>
- parseMarkdownFile(file, partials, typedocs, prompts, inManifest, 'docs'),
+ parseMarkdownFile(file, partials, tooltips, typedocs, prompts, inManifest, 'docs'),
)
docsMap.set(file.href, markdownFile)
@@ -776,6 +776,7 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
const foundLinks: Set = new Set()
const foundPartials: Set = new Set()
const foundTypedocs: Set = new Set()
+ const foundTooltips: Set = new Set()
const sdks = [...(doc.sdk ?? []), ...(doc.distinctSDKVariants ?? [])]
@@ -800,6 +801,11 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
foundPartials.add(partial)
}),
)
+ .use(
+ checkTooltips(config, validatedTooltips, doc.file, { reportWarnings: false, embed: true }, (tooltip) => {
+ foundTooltips.add(tooltip)
+ }),
+ )
.use(
checkTypedoc(
config,
@@ -842,7 +848,11 @@ export async function build(config: BuildConfig, store: Store = createBlankStore
.filter((typedoc) => foundTypedocs.has(typedoc.path))
.reduce((acc, { links }) => new Set([...acc, ...links]), foundTypedocs)
- const allLinks = new Set([...foundLinks, ...partialsLinks, ...typedocsLinks])
+ const tooltipsLinks = validatedTooltips
+ .filter((tooltip) => foundTooltips.has(tooltip.path))
+ .reduce((acc, { links }) => new Set([...acc, ...links]), foundTooltips)
+
+ const allLinks = new Set([...foundLinks, ...partialsLinks, ...typedocsLinks, ...tooltipsLinks])
allLinks.forEach((link) => {
markDirty(doc.file.filePath, link)
@@ -959,6 +969,7 @@ ${yaml.stringify({
.use(remarkMdx)
.use(validateLinks(config, docsMap, doc.file.filePath, 'docs', undefined, doc.file.href))
.use(checkPartials(config, partials, doc.file, { reportWarnings: true, embed: true }))
+ .use(checkTooltips(config, tooltips, doc.file, { reportWarnings: true, embed: true }))
.use(checkTypedoc(config, typedocs, doc.file.filePath, { reportWarnings: true, embed: true }))
.use(checkPrompts(config, prompts, doc.file, { reportWarnings: true, update: true }))
.use(embedLinks(config, docsMap, sdks, undefined, doc.file.href))
@@ -1116,13 +1127,6 @@ ${yaml.stringify({
abortSignal?.throwIfAborted()
- if (config.tooltips) {
- await writeTooltipsToDist(validatedTooltips)
- console.info(`✓ Wrote ${validatedTooltips.length} tooltips to disk`)
- }
-
- abortSignal?.throwIfAborted()
-
if (config.llms?.fullPath || config.llms?.overviewPath) {
const outputtedDocsFiles = listOutputDocsFiles(config, store.writtenFiles, mdxFilePaths)
diff --git a/scripts/lib/error-messages.ts b/scripts/lib/error-messages.ts
index a896e6f943..a2ea951e02 100644
--- a/scripts/lib/error-messages.ts
+++ b/scripts/lib/error-messages.ts
@@ -77,6 +77,7 @@ export const errorMessages = {
// Tooltip errors
'tooltip-read-error': (path: string): string => `Failed to read in ${path} from tooltips file`,
'tooltip-parse-error': (path: string): string => `Failed to parse the content of ${path}`,
+ 'tooltip-not-found': (src: string): string => `Tooltip ${src} not found`,
// Typedoc errors
'typedoc-folder-not-found': (path: string): string =>
diff --git a/scripts/lib/markdown.ts b/scripts/lib/markdown.ts
index c204df5b4d..c7f4729411 100644
--- a/scripts/lib/markdown.ts
+++ b/scripts/lib/markdown.ts
@@ -27,6 +27,7 @@ import type { SDK } from './schemas'
import { markDocumentDirty, type Store } from './store'
import { documentHasIfComponents } from './utils/documentHasIfComponents'
import { extractHeadingFromHeadingNode } from './utils/extractHeadingFromHeadingNode'
+import { checkTooltips } from './plugins/checkTooltips'
const calloutRegex = new RegExp(/^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|QUIZ)(\s+[0-9a-z-]+)?\]$/)
@@ -35,6 +36,7 @@ export const parseInMarkdownFile =
async (
file: DocsFile & { content?: string },
partials: { path: string; content: string; node: Node }[],
+ tooltips: { path: string; content: string; node: Node }[],
typedocs: { path: string; content: string; node: Node }[],
prompts: Prompt[],
inManifest: boolean,
@@ -79,6 +81,11 @@ export const parseInMarkdownFile =
markDirty(file.filePath, partial)
}),
)
+ .use(
+ checkTooltips(config, tooltips, file, { reportWarnings: true, embed: false }, (tooltip) => {
+ markDirty(file.filePath, tooltip)
+ }),
+ )
.use(
checkTypedoc(config, typedocs, file.filePath, { reportWarnings: true, embed: false }, (typedoc) => {
markDirty(file.filePath, typedoc)
diff --git a/scripts/lib/plugins/checkTooltips.ts b/scripts/lib/plugins/checkTooltips.ts
new file mode 100644
index 0000000000..8e06362865
--- /dev/null
+++ b/scripts/lib/plugins/checkTooltips.ts
@@ -0,0 +1,95 @@
+// This validator manages the tooltips in the docs
+// based on the options passed through it can
+// - only report warnings if something ain't right
+// - only embed the tooltips contents in to the markdown
+// - both report warnings and embed the tooltips contents
+
+import type { Node } from 'unist'
+import { map as mdastMap } from 'unist-util-map'
+import type { VFile } from 'vfile'
+import type { BuildConfig } from '../config'
+import { safeMessage } from '../error-messages'
+import type { DocsFile } from '../io'
+import { removeMdxSuffix } from '../utils/removeMdxSuffix'
+import { u as mdastBuilder } from 'unist-builder'
+
+export const checkTooltips =
+ (
+ config: BuildConfig,
+ tooltips: {
+ node: Node
+ path: string
+ }[],
+ file: DocsFile,
+ options: {
+ reportWarnings: boolean
+ embed: boolean
+ },
+ foundTooltip?: (tooltip: string) => void,
+ ) =>
+ () =>
+ (tree: Node, vfile: VFile) => {
+ return mdastMap(tree, (node) => {
+ // Tooltips are written as links with the format [trigger](!content)
+ // We need to check if the node is a link
+ if (node.type !== 'link') return node
+ if (!('url' in node)) return node
+ if (!('children' in node)) return node
+ if (typeof node.url !== 'string') return node
+
+ // Then, check if the link is a tooltip (starts with !)
+ // [trigger text](!content)
+ if (!node.url.startsWith('!')) return node
+
+ // Access the link properties
+ // url of the link = e.g. '!content'
+ // children (the text content of the link) = e.g. 'trigger text'
+ const url = node.url
+ const children = node.children
+
+ // The tooltip content exists in a MDX file e.g. '_tooltips/content.mdx'
+ // We need to remove the ! to get the file path e.g. 'content'
+ const tooltipSrc = url.substring(1)
+
+ const tooltip = tooltips.find((tooltip) => tooltip.path === `_tooltips/${removeMdxSuffix(tooltipSrc)}.mdx`)
+
+ if (tooltip === undefined) {
+ if (options.reportWarnings === true) {
+ safeMessage(
+ config,
+ vfile,
+ file.filePath,
+ 'docs',
+ 'tooltip-not-found',
+ [removeMdxSuffix(tooltipSrc)],
+ node.position,
+ )
+ }
+ return node
+ }
+
+ foundTooltip?.(`${removeMdxSuffix(tooltipSrc)}.mdx`)
+
+ if (options.embed === true) {
+ return Object.assign(
+ node,
+ mdastBuilder('mdxJsxTextElement', {
+ name: 'Tooltip',
+ attributes: [],
+ children: [
+ mdastBuilder('mdxJsxTextElement', {
+ name: 'TooltipTrigger',
+ children: children,
+ }),
+ mdastBuilder('mdxJsxTextElement', {
+ name: 'TooltipContent',
+ children: [tooltip.node],
+ }),
+ ],
+ }),
+ )
+ }
+
+ return node
+ })
+ }
diff --git a/scripts/lib/store.ts b/scripts/lib/store.ts
index d6e2a9b51a..42d4e66ffe 100644
--- a/scripts/lib/store.ts
+++ b/scripts/lib/store.ts
@@ -112,7 +112,7 @@ export const invalidateFile =
if (store.tooltips.has(relativeTooltipPath)) {
store.tooltips.delete(relativeTooltipPath)
- const adjacent = store.dirtyDocMap.get(relativeTooltipPath)
+ const adjacent = store.dirtyDocMap.get(`_tooltips/${relativeTooltipPath}`)
if (adjacent && invalidateAdjacentDocs) {
const invalidate = invalidateFile(store, config)
diff --git a/scripts/lib/tooltips.ts b/scripts/lib/tooltips.ts
index 3a740c00a2..0537d3d870 100644
--- a/scripts/lib/tooltips.ts
+++ b/scripts/lib/tooltips.ts
@@ -85,17 +85,3 @@ export const readTooltipsMarkdown = (config: BuildConfig, store: Store) => async
return Promise.all(paths.map(async (markdownPath) => tooltipsCache(markdownPath, () => read(markdownPath))))
}
-
-type Tooltips = Awaited>>
-
-export const writeTooltips = (config: BuildConfig, store: Store) => async (tooltips: Tooltips) => {
- if (!config.tooltips) {
- throw new Error('Tooltips are not enabled')
- }
-
- const write = writeDistFile(config, store)
-
- for (const tooltip of tooltips) {
- await write(tooltip.path, tooltip.vfile.value as string)
- }
-}
diff --git a/styleguides/STYLEGUIDE.md b/styleguides/STYLEGUIDE.md
index 7f74859495..27bb96b5ab 100644
--- a/styleguides/STYLEGUIDE.md
+++ b/styleguides/STYLEGUIDE.md
@@ -8,7 +8,7 @@ These are the guidelines we use to write our docs.
Try to keep things in alphabetic order, except Next.js, React, and JavaScript are prioritized as these are our core SDKs. For example, our SDK selector prioritizes Next.js, React, and Javascript, and then alphabetizes the rest of the SDK's. Another example is that whenever there is a `` component, the `items` should follow this same rule.
-### De-dupe reference links
+### De-dupe reference links and tooltips
When mentioning a documented component, function, etc, multiple times on a page, link to the reference documentation on the **first mention** of that item. The exception to this rule is when the reference is mentioned under a different heading. In that case, link to the reference documentation again.
@@ -18,6 +18,8 @@ When mentioning a documented component, function, etc, multiple times on a page,
> ✅
> The [`currentUser()`](https://clerk.com/docs/references/nextjs/current-user) helper will return the [`User`](https://clerk.com/docs/references/javascript/user) object of the currently active user. The following example uses the `currentUser()` helper to access the `User` object for the authenticated user.
+This same rule applies to tooltips.
+
### Use sentence-case for titles
> ❌