Skip to Content
Clerk logo

Clerk Docs

Ctrl + K
Go to clerk.com

User metadata

Metadata allows for custom data to be saved on the User object. There are three types of metadata: "unsafe", "public", and "private".

MetadataFrontend APIBackend API
PrivateNo read or write accessRead & write access
PublicRead accessRead & write access
UnsafeRead & write accessRead & write access

Metadata is limited to 8kb maximum.

Private metadata

Private metadata is only accessible by the backend, which makes this useful for storing sensitive data that you don't want to expose to the frontend. For example, you could store a user's Stripe customer ID.

Setting private metadata

app/route/private.ts
import { NextResponse } from 'next/server'; import { clerkClient } from "@clerk/nextjs"; export async function POST() { const { stripeId, userId } = await body.json(); await clerkClient.users.updateUserMetadata(userId, { privateMetadata: { stripeId: stripeId } }); return NextResponse.json({ success: true }); }
pages/api/private.ts
import { clerkClient } from "@clerk/nextjs"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { stripeId, userId } = req.body; await clerkClient.users.updateUserMetadata(userId, { privateMetadata: { stripeId: stripeId } }) res.status(200).json({ success: true }); }

Retrieving private metadata

You can retrieve the private metadata for a user by using the getUser method. This method will return the User object which contains the private metadata.

app/private/route.ts
import { NextResponse } from 'next/server'; import { clerkClient } from "@clerk/nextjs"; export async function GET(request: Request) { const { stripeId, userId } = await request.body.json(); const user = await clerkClient.users.getUser(userId) return NextResponse.json(user.privateMetadata); }
pages/api/private.ts
import { clerkClient } from "@clerk/nextjs"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { userId } = await req.body.json(); const user = await clerkClient.users.getUser(userId) res.status(200).json(user.privateMetadata); }

Public metadata

Public metadata is accessible by both the frontend and the backend. This is useful for storing data that you want to expose to the frontend, but don't want the user to be able to modify. For example, you could store a custom role for a user.

Setting public metadata

app/route/public.ts
import { NextResponse } from 'next/server'; import { clerkClient } from "@clerk/nextjs"; export async function POST() { const { role, userId } = await body.json(); await clerkClient.users.updateUserMetadata(userId, { publicMetadata: { role } }) return NextResponse.json({ success: true }); }
pages/api/public.ts
import { clerkClient } from "@clerk/nextjs"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { role, userId } = req.body; await clerkClient.users.updateUserMetadata(userId, { publicMetadata: { role } }) res.status(200).json({ success: true }); }

Retrieving public metadata

There are multiple ways to retrieve public metadata. It is available on the User object returned by the useUser hook, and it can also be attached to the session token to be retrieved with auth() and useAuth(). If you need to retrieve public metadata frequently in the backend, the best option is to attach it to the session token and retrieve it from the session token.

To read about customizing your session token and retrieving that data, check out our session token customization guide.

Unsafe metadata

If you need to implement custom fields that will be attached to the User object from the frontend (using our ClerkJS library or the Frontend API(opens in a new tab)), you should choose the unsafeMetadata property.

One common use case for this attribute is to implement custom fields that will be attached to the User object.

Clerk has named this type of metadata "unsafe" since it can be set and accessed by both the Frontend API and the Backend API. This provides a quick method to add custom attributes to a user. These attributes will be stored on the User object and will be available for access at all times.

The "unsafe" custom attributes can be set upon sign-up when creating or updating a SignUp object. After a successful sign-up, these attributes will be copied to the User object. From that point on they can be accessed as a direct attribute of the User object.

Setting unsafe metadata

Updating this value overrides the previous value; it does not merge. To perform a merge, you can pass something like { …user.unsafeMetadata, …newData } to this parameter.

Using the Backend API

app/route/private.ts
import { NextResponse } from 'next/server'; import { clerkClient } from "@clerk/nextjs"; export async function POST() { const { stripeId, userId } = await body.json(); await clerkClient.users.updateUserMetadata(userId, { unsafeMetadata: { "birthday": "11-30-1969" } }); return NextResponse.json({ success: true }); }
pages/api/private.ts
import { clerkClient } from "@clerk/nextjs"; import { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { stripeId, userId } = req.body; await clerkClient.users.updateUserMetadata(userId, { unsafeMetadata: { "birthday": "11-30-1969" } }) res.status(200).json({ success: true }); }

Using the Frontend SDKs

app/route/unsafe.tsx
"use client" import {useUser} from "@clerk/nextjs"; import {useState} from "react"; export default function UnSafePage() { const { user } = useUser(); const [birthday, setBirthday] = useState(""); return( <div> <input type="text" value={birthday} onChange={(e) => setBirthday(e.target.value)} /> <button onClick={() => { user.update({ unsafeMetadata: { birthday } }) }}>Update Birthday</button> </div> ) }
pages/unsafe.tsx
import {useUser} from "@clerk/nextjs"; import {useState} from "react"; export default function UnSafePage() { const { user } = useUser(); const [birthday, setBirthday] = useState("") return( <div> <input type="text" value={birthday} onChange={(e) => setBirthday(e.target.value)} /> <button onClick={() => { user.update({ unsafeMetadata: { birthday } }) }}>Update Birthday</button> </div> ) }

Retrieving unsafe metadata

There are multiple ways to retrieve unsafe metadata. It is available in the User object returned by the useUser() hook. It can also be attached to a session token, which can be retrieved with the auth() helper in Next.js applications, or with the useAuth() hook. If you need to retrieve unsafe metadata frequently in the backend, the best option is to attach it to the session token and retrieve it from the session token.

To read about customization your session token and retrieving that data, check out our session token customization guide.

Last updated on March 26, 2024

What did you think of this content?

Clerk © 2024