Build a custom flow for creating Organizations
This guide demonstrates how to use Clerk's API to build a custom flow for creating Organizations.
The following example uses these hooks:
-
The useOrganizationList() hook to access the
createOrganization()method. This method is used to create a new Organization with the provided name. -
The useOrganizationCreationDefaults() hook to pre-populate the form with a suggested organization name based on your application's default naming rules, and to display a warning if an organization with that name or domain already exists.
src /components /CreateOrganizationWithWarning.tsx import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/clerk-react' import { FormEventHandler, useEffect, useState } from 'react' export default function CreateOrganizationWithWarning() { const { isLoaded, createOrganization } = useOrganizationList() const { data: defaults, isLoading } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoading) return <div>Loading...</div> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => { e.preventDefault() try { await createOrganization?.({ name: organizationName }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } } return ( <form onSubmit={handleSubmit}> <input type="text" value={organizationName} onChange={(e) => setOrganizationName(e.target.value)} placeholder="Organization name" /> {showWarning && ( <p style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </p> )} <button type="submit">Create organization</button> </form> ) }app /components /CreateOrganizationWithWarning.tsx import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/react-router' import { FormEventHandler, useEffect, useState } from 'react' export default function CreateOrganizationWithWarning() { const { isLoaded, createOrganization, setActive } = useOrganizationList() const { data: defaults, isLoading } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoading) return <div>Loading...</div> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => { e.preventDefault() try { const newOrganization = await createOrganization?.({ name: organizationName }) // Set the created Organization as the Active Organization if (newOrganization) setActive({ organization: newOrganization.id }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } } return ( <form onSubmit={handleSubmit}> <input type="text" value={organizationName} onChange={(e) => setOrganizationName(e.target.value)} placeholder="Organization name" /> {showWarning && ( <p style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </p> )} <button type="submit">Create organization</button> </form> ) }src /components /CreateOrganizationWithWarning.tsx import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/chrome-extension' import { FormEventHandler, useEffect, useState } from 'react' export default function CreateOrganizationWithWarning() { const { isLoaded, createOrganization, setActive } = useOrganizationList() const { data: defaults, isLoading } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoading) return <div>Loading...</div> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => { e.preventDefault() try { const newOrganization = await createOrganization?.({ name: organizationName }) // Set the created Organization as the Active Organization if (newOrganization) setActive({ organization: newOrganization.id }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } } return ( <form onSubmit={handleSubmit}> <input type="text" value={organizationName} onChange={(e) => setOrganizationName(e.target.value)} placeholder="Organization name" /> {showWarning && ( <p style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </p> )} <button type="submit">Create organization</button> </form> ) }app /components /CreateOrganizationWithWarning.tsx import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/tanstack-react-start' import { FormEventHandler, useEffect, useState } from 'react' export default function CreateOrganizationWithWarning() { const { isLoaded, createOrganization, setActive } = useOrganizationList() const { data: defaults, isLoading } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoading) return <div>Loading...</div> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => { e.preventDefault() try { const newOrganization = await createOrganization?.({ name: organizationName }) // Set the created Organization as the Active Organization if (newOrganization) setActive({ organization: newOrganization.id }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } } return ( <form onSubmit={handleSubmit}> <input type="text" value={organizationName} onChange={(e) => setOrganizationName(e.target.value)} placeholder="Organization name" /> {showWarning && ( <p style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </p> )} <button type="submit">Create organization</button> </form> ) }components /CreateOrganizationWithWarning.tsx import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/clerk-expo' import { useEffect, useState } from 'react' import { Text, TextInput, TouchableOpacity, View } from 'react-native' export default function CreateOrganizationWithWarning() { const { isLoaded, createOrganization, setActive } = useOrganizationList() const { data: defaults, isLoading } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoading) return <Text>Loading...</Text> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit = async () => { try { const newOrganization = await createOrganization?.({ name: organizationName }) // Set the created Organization as the Active Organization if (newOrganization) setActive({ organization: newOrganization.id }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } } return ( <View> <TextInput value={organizationName} onChangeText={setOrganizationName} placeholder="Organization name" /> {showWarning && ( <Text style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </Text> )} <TouchableOpacity onPress={handleSubmit}> <Text>Create organization</Text> </TouchableOpacity> </View> ) }app /components /CreateOrganization.tsx 'use client' import { useOrganizationCreationDefaults, useOrganizationList } from '@clerk/nextjs' import { FormEventHandler, useEffect, useState } from 'react' export default function CreateOrganization() { const { isLoaded, createOrganization, setActive } = useOrganizationList() const { data: defaults, isLoading: isLoadingDefaults } = useOrganizationCreationDefaults() const [organizationName, setOrganizationName] = useState('') // Pre-populate the form with suggested organization name useEffect(() => { if (defaults?.form.name) { setOrganizationName(defaults.form.name) } }, [defaults?.form.name]) if (!isLoaded || isLoadingDefaults) return <p>Loading...</p> // Check if an organization with this name/domain already exists const advisory = defaults?.advisory const showWarning = advisory?.code === 'organization_already_exists' const existingOrgName = advisory?.meta?.organization_name const existingOrgDomain = advisory?.meta?.organization_domain const handleSubmit: FormEventHandler<HTMLFormElement> = async (e) => { e.preventDefault() try { const newOrganization = await createOrganization?.({ name: organizationName }) // Set the created Organization as the Active Organization if (newOrganization) setActive({ organization: newOrganization.id }) } catch (err) { // See https://clerk.com/docs/guides/development/custom-flows/error-handling // for more info on error handling console.error(JSON.stringify(err, null, 2)) } setOrganizationName('') } return ( <form onSubmit={handleSubmit}> <input type="text" value={organizationName} onChange={(e) => setOrganizationName(e.currentTarget.value)} placeholder="Organization name" /> {showWarning && ( <p style={{ color: 'orange' }}> An organization "{existingOrgName}" already exists for the domain "{existingOrgDomain}". </p> )} <button type="submit">Create organization</button> </form> ) }
The following example uses the clerk.createOrganization() method to create a new Organization with the provided name.
Use the tabs to view the code necessary for the index.html and main.js files.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
</head>
<body>
<div id="app"></div>
<h1>Create an organization</h1>
<form id="create-organization">
<label for="name">Name</label>
<input id="name" name="name" />
<button>Create organization</button>
</form>
<script type="module" src="/src/main.js" async crossorigin="anonymous"></script>
</body>
</html>import { Clerk } from '@clerk/clerk-js'
const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!pubKey) {
throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file')
}
const clerk = new Clerk(pubKey)
await clerk.load()
if (clerk.isSignedIn) {
const form = document.getElementById('create-organization')
form.addEventListener('submit', async (e) => {
e.preventDefault()
const inputEl = document.getElementById('name')
if (!inputEl) {
// ... handle empty input
return
}
try {
await clerk.createOrganization({ name: inputEl.value })
if (newOrganization) clerk.setActive({ organization: newOrganization.id })
} catch (error) {
console.log('An error occurred:', error)
}
})
} else {
// If there is no active user, mount Clerk's <SignIn />
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
clerk.mountSignIn(signInDiv)
}Examples for this SDK aren't available yet. For now, try switching to a supported SDK, such as Next.js, and converting the code to fit your SDK.
Feedback
Last updated on
Edit on GitHub