βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β π shadcn/directory/clerk/clerk-docs/guides/development/sdk-development/frontend-only β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β
While ClerkJS can be used in any browser context and framework, realistically users expect to consume its features through the conventions and syntax of their framework of choice. For example, @clerk/clerk-react turns ClerkJS into React components, @clerk/astro into Astro components, and so on.
In non-browser environments, youβll need to re-implement the Clerk class in the SDKβs programming language, interacting with the FAPI.
Client{{ target: '_blank' }}, Session{{ target: '_blank' }}, User{{ target: '_blank' }}, and Organization{{ target: '_blank' }} properties through the frameworkβs choice of state managementWhile the implementation details will vary for each SDK, there are certain steps you'll have to go through in any case. Consider the steps below a rough guidance on what needs to be done, and also remember to follow the conventions.
<Steps> ### Create a Clerk instance[!NOTE] The code blocks below will be written in pseudo-code. If you're looking for real-world examples, have a look at these repositories:
@clerk/clerk-react,@clerk/astro
Create a Clerk instance that will only be invoked once (e.g. following the singleton pattern). During its initialization you'll execute the following steps.
import { runOnce } from './utils'
// States accessible to other parts of your SDK and/or its users
import { $clerk, $state } from './stores'
export const createClerkInstance = runOnce(createClerkInstanceInternal)
async function createClerkInstanceInternal(options) {
let clerkJSInstance = window.Clerk
}
In order to make Clerk available on the window object, your SDK needs to load ClerkJS. Call loadClerkJsScript() from @clerk/shared.
import { loadClerkJSScript } from '@clerk/shared/loadClerkJSScript'
import { runOnce } from './utils'
import { $clerk, $state } from './stores'
export const createClerkInstance = runOnce(createClerkInstanceInternal)
async function createClerkInstanceInternal(options) {
let clerkJSInstance = window.Clerk
if (!clerkJSInstance) {
await loadClerkJSScript(options)
if (!window.Clerk) {
throw new Error('Failed to download latest ClerkJS.')
}
clerkJSInstance = window.Clerk
}
if (!$clerk.get()) {
$clerk.set(clerkJSInstance)
}
}
window.Clerk.load()By calling window.Clerk.load() the Clerk class is initialized and your SDK now has access to all its functionality.
import { loadClerkJSScript } from '@clerk/shared/loadClerkJSScript'
import { runOnce } from './utils'
import { $clerk, $state } from './stores'
export const createClerkInstance = runOnce(createClerkInstanceInternal)
async function createClerkInstanceInternal(options) {
let clerkJSInstance = window.Clerk
if (!clerkJSInstance) {
// loadClerkJSScript() codepath
}
if (!$clerk.get()) {
$clerk.set(clerkJSInstance)
}
await clerkJSInstance.load(options)
}
Expose properties to the internal state (e.g. a useState in React) through adding event listeners.
import { loadClerkJSScript } from '@clerk/shared/loadClerkJSScript'
import { runOnce } from './utils'
import { $clerk, $state } from './stores'
export const createClerkInstance = runOnce(createClerkInstanceInternal)
async function createClerkInstanceInternal(options) {
let clerkJSInstance = window.Clerk
if (!clerkJSInstance) {
// loadClerkJSScript() codepath
}
await clerkJSInstance.load(options)
if (!$clerk.get()) {
$clerk.set(clerkJSInstance)
}
clerkJSInstance.addListener((resources) => {
$state.setKey('client', resources.client)
$state.setKey('session', resources.session)
$state.setKey('user', resources.user)
$state.setKey('organization', resources.organization)
})
}
Build out the components that your users will utilize in their app. Call the mount() function when the component is in view/mounts and the unmount() function when the component is unmounted. These functions are described in the Clerk class components.
Use the idiomatic way of your framework for doing this. If you can abstract these repetitions into a re-usable hook/directive, then do that.
import { $clerk } from './stores'
const SignInComponent = (props: SignInProps) => {
const el = ref()
function onMount() {
$clerk.mountSignIn(el, props)
}
function onUnMount() {
$clerk.unmountSignIn(el)
}
}
</Steps>β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ