useOrganization

Access and manage organization data in your components

useOrganization Hook ๐Ÿข

The useOrganization hook is your primary way to access and manage organization data in client components! โœจ

What It Does ๐ŸŽฏ

The hook provides:

  • ๐Ÿข Current organization data (name, slug, plan, etc.)
  • ๐Ÿ‘ฅ User's membership info (role, permissions)
  • ๐Ÿ”„ Loading and error states
  • ๐Ÿ” Ability to refresh data
  • ๐Ÿ”€ Function to switch organizations

Client Components Only

This hook uses SWR and must be used in client components. Add 'use client' at the top of your file!

Basic Usage ๐Ÿ“

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'

export default function Dashboard() {
  const { organization, isLoading, error } = useOrganization()

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error loading organization</div>

  return (
    <div>
      <h1>Welcome to {organization.name}</h1>
      <p>Your role: {organization.role}</p>
    </div>
  )
}

Return Values ๐Ÿ“ฆ

The hook returns an object with the following properties:

{
  organization: {
    id: string
    name: string
    slug: string
    image?: string
    onboardingDone: boolean
    role: 'owner' | 'admin' | 'user'
    plan: {
      id: string
      name: string
      codename: string
      default: boolean
      quotas: object
      requiredCouponCount: number
    } | null
  }
  isLoading: boolean
  error: Error | null
  mutate: () => void
  switchOrganization: (organizationId: string) => Promise<void>
}

Plan Can Be Null

The plan property can be null if the organization doesn't have a plan assigned yet. Always check for null before accessing plan properties!

Common Use Cases ๐Ÿš€

Display Organization Info

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'

export function OrganizationHeader() {
  const { organization, isLoading } = useOrganization()

  if (isLoading) return <div>Loading...</div>

  return (
    <div className="flex items-center gap-4">
      {organization.image && (
        <img 
          src={organization.image} 
          alt={organization.name}
          className="w-10 h-10 rounded"
        />
      )}
      <div>
        <h2 className="font-bold">{organization.name}</h2>
        <p className="text-sm text-gray-500">
          {organization.plan?.name || 'No Plan'} Plan
        </p>
      </div>
    </div>
  )
}

Check User Role

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'

export function MembersButton() {
  const { organization } = useOrganization()
  
  const isAdmin = ['admin', 'owner'].includes(organization.role)

  if (!isAdmin) return null

  return (
    <button>
      Invite Members
    </button>
  )
}

Access Plan Quotas

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'

export function ProjectsPage() {
  const { organization } = useOrganization()
  
  if (!organization.plan) {
    return <div>No plan assigned</div>
  }
  
  const { projects } = organization.plan.quotas
  const currentProjects = 5 // Fetch from your data
  const canCreateMore = currentProjects < projects

  return (
    <div>
      <p>Projects: {currentProjects} / {projects}</p>
      {!canCreateMore && (
        <p className="text-red-500">
          Upgrade your plan to create more projects
        </p>
      )}
    </div>
  )
}

Refresh Organization Data

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'
import { toast } from 'sonner'

export function OrganizationSettings() {
  const { organization, mutate } = useOrganization()

  const updateSettings = async (settings: any) => {
    try {
      await fetch(`/api/app/org/${organization.slug}/settings`, {
        method: 'PATCH',
        body: JSON.stringify(settings)
      })
      
      // Refresh organization data
      mutate()
      toast.success('Settings updated!')
    } catch (error) {
      toast.error('Failed to update settings')
    }
  }

  return (
    <div>
      <button onClick={() => updateSettings({ name: 'New Name' })}>
        Update Settings
      </button>
    </div>
  )
}

Switch Organizations

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'
import { useRouter } from 'next/navigation'

export function OrganizationSwitcher({ organizations }) {
  const { organization, switchOrganization } = useOrganization()

  const handleSwitch = async (organizationId: string) => {
    await switchOrganization(organizationId)
    // Redirects to /app automatically
  }

  return (
    <select
      value={organization.id}
      onChange={(e) => handleSwitch(e.target.value)}
      className="border rounded px-3 py-2"
    >
      {organizations.map(org => (
        <option key={org.id} value={org.id}>
          {org.name}
        </option>
      ))}
    </select>
  )
}

Loading States ๐Ÿ”„

Always handle loading and error states:

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'
import { Skeleton } from '@/components/ui/skeleton'

export default function Dashboard() {
  const { organization, isLoading, error } = useOrganization()

  // Loading state with skeleton
  if (isLoading) {
    return (
      <div className="space-y-4">
        <Skeleton className="h-12 w-full" />
        <Skeleton className="h-32 w-full" />
        <Skeleton className="h-32 w-full" />
      </div>
    )
  }

  // Error state
  if (error) {
    return (
      <div className="p-4 bg-red-50 text-red-600 rounded">
        <p>Failed to load organization</p>
        <button onClick={() => window.location.reload()}>
          Retry
        </button>
      </div>
    )
  }

  // Success state
  return (
    <div>
      <h1>{organization.name}</h1>
      {/* Your content */}
    </div>
  )
}

Real-World Example ๐Ÿ’ผ

Complete dashboard component with organization data:

'use client'

import { useOrganization } from '@/lib/organizations/useOrganization'
import { Skeleton } from '@/components/ui/skeleton'

export default function OrganizationDashboard() {
  const { organization, isLoading, error, mutate } = useOrganization()

  if (isLoading) {
    return (
      <div className="p-6 space-y-4">
        <Skeleton className="h-8 w-64" />
        <Skeleton className="h-32 w-full" />
      </div>
    )
  }

  if (error) {
    return (
      <div className="p-6">
        <div className="p-4 bg-red-50 text-red-600 rounded">
          Error loading organization
        </div>
      </div>
    )
  }

  const isOwner = organization.role === 'owner'
  const isAdmin = ['admin', 'owner'].includes(organization.role)

  return (
    <div className="p-6 space-y-6">
      {/* Header */}
      <div>
        <h1 className="text-3xl font-bold">{organization.name}</h1>
        <p className="text-gray-500">
          {organization.plan?.name || 'No Plan'} ยท {organization.role}
        </p>
      </div>

      {/* Stats */}
      <div className="grid grid-cols-3 gap-4">
        <div className="p-4 border rounded">
          <p className="text-sm text-gray-500">Plan</p>
          <p className="text-2xl font-bold">
            {organization.plan?.name || 'No Plan'}
          </p>
        </div>
        <div className="p-4 border rounded">
          <p className="text-sm text-gray-500">Your Role</p>
          <p className="text-2xl font-bold capitalize">
            {organization.role}
          </p>
        </div>
        <div className="p-4 border rounded">
          <p className="text-sm text-gray-500">Onboarding</p>
          <p className="text-2xl font-bold">
            {organization.onboardingDone ? 'โœ“' : 'Pending'}
          </p>
        </div>
      </div>

      {/* Actions */}
      <div className="flex gap-2">
        {isAdmin && (
          <>
            <button className="px-4 py-2 bg-blue-500 text-white rounded">
              Invite Members
            </button>
            <button className="px-4 py-2 border rounded">
              Settings
            </button>
          </>
        )}
        {isOwner && (
          <button className="px-4 py-2 border rounded">
            Billing
          </button>
        )}
      </div>
    </div>
  )
}

Best Practices ๐Ÿ’ก

1. Always Handle Loading States

if (isLoading) return <LoadingSkeleton />

2. Handle Errors Gracefully

if (error) return <ErrorMessage />

3. Check Role Before Actions

const canInvite = ['admin', 'owner'].includes(organization.role)
if (!canInvite) return null

4. Refresh After Mutations

await updateOrganization()
mutate() // Refresh data

5. Use TypeScript

import type { Organization } from '@/lib/organizations/types'

const { organization }: { organization: Organization } = useOrganization()

Troubleshooting ๐Ÿ”ง

"organization is undefined"

Make sure the component is inside an organization route:

// โœ… Good - Inside /app/org/[slug]/
'use client'
import { useOrganization } from '@/lib/organizations/useOrganization'

export default function Page() {
  const { organization } = useOrganization()
  // organization will be available
}

"Must be used in client component"

Add 'use client' at the top:

'use client' // Add this!

import { useOrganization } from '@/lib/organizations/useOrganization'

Data Not Updating

Call mutate() after making changes:

const { mutate } = useOrganization()

await updateOrganization()
mutate() // Force refresh

Switch Organization Not Working

The function automatically redirects to /app:

const { switchOrganization } = useOrganization()

// This will switch and redirect automatically
await switchOrganization(newOrganizationId)

// No need to manually navigate - it's handled for you!

Toast Notifications

The switchOrganization function shows toast notifications automatically for loading, success, and error states!

API Reference ๐Ÿ“š

Parameters

The hook takes no parameters.

Returns

PropertyTypeDescription
organizationUserOrganizationWithPlanCurrent organization data with plan and role
isLoadingbooleanTrue while loading organization data
errorError | nullError object if request failed
mutate() => voidFunction to refresh organization data
switchOrganization(organizationId: string) => Promise<void>Switch to a different organization (shows toast and redirects)

Summary ๐Ÿ“‹

You now know how to:

  • โœ… Use the useOrganization hook in components
  • โœ… Access organization and membership data
  • โœ… Handle loading and error states
  • โœ… Check user roles and permissions
  • โœ… Refresh organization data
  • โœ… Switch between organizations
  • โœ… Build role-based UIs

Ready to Build

The useOrganization hook gives you everything you need to build organization-aware components! ๐ŸŽ‰