Skip to content

Full-featured User Authentication Made Easy with Clerk

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Intro

Authentication is hard. It’s just another example of one of the things we deal with as web developers that can be solved in 100 different ways, and requires a well-thought-out decision. Thinking and decision making: my week in a nutshell. The first decision you need to make is whether you are going to roll your own authentication or use a third-party provider. If you decide to roll your own, you have a whole lot more thinking and decision-making ahead. There are a lot of considerations and specialized security-related knowledge required for this path if you intend to build an application that is secure. This post is going to cover a newer third-party provider option named Clerk.

Clerk overview

Clerk has been gaining popularity because it offers a fantastic developer experience. It comes with a first-class React integration that includes most of the popular React Frameworks. These integrations are powered by a full-featured, modern authentication service.

Auth features

Clerk offers an impressive array of authentication features that cater to a wide range of use cases and security requirements. Let's dive into these features and explore their benefits.

  • User management: Clerk provides a comprehensive user management system that allows you to easily manage and monitor user accounts, including registration, login, and profile updates. This saves you time and effort in building and maintaining a custom user management solution.
  • Organizations: The Organizations feature enables you to group users into separate entities, such as companies or teams. This is particularly useful for applications that require different levels of access or collaboration among various groups of users. With Clerk's Organizations, you can easily manage permissions, roles, and billing for each organization, streamlining your app's administrative tasks.
  • Email and SMS authentication: Clerk supports both email and SMS authentication, allowing users to securely authenticate using their preferred method. This flexibility enhances the user experience and helps to ensure that your application is accessible to a wider audience.
  • Password-less authentication: With Magic links and one-time passwords, Clerk offers password-less authentication options that eliminate the need for users to remember complex passwords. This not only improves the user experience, but also enhances security by reducing the risk of password-related attacks.
  • Social login: Clerk's Social login feature allows users to authenticate using their existing social media accounts, such as Google or Facebook. This simplifies the registration and login process for users, while also potentially increasing conversion rates for your application.
  • Multi-factor authentication (MFA): MFA adds an extra layer of security by requiring users to provide multiple forms of identification during the authentication process. With Clerk, you can easily enable MFA for your application, ensuring that your users' accounts are better protected against unauthorized access.
  • Multi-sessions: Clerk's multi-session feature allows users to be logged into multiple accounts simultaneously, similar to popular apps like Figma. This is particularly useful for users who need to switch between personal and work accounts frequently, providing a seamless and efficient user experience.
  • Device management: Device management is a powerful feature that allows users to view and manage all the devices they've used to access your application. This level of control is typically found in solutions provided by tech giants, and its inclusion in Clerk's feature set demonstrates their commitment to providing a robust and secure authentication experience.
  • Password leak protection: Clerk's password leak protection feature informs users if their password has been detected in a data breach. By proactively alerting users to potential security risks, this feature helps to protect your users' accounts and further sets Clerk apart from its competitors.

Organizations, Multi-Sessions, and Device Management are features that really set Clerk apart from a lot of their competitors. If I’m working on a new application that requires any of these, I would be looking to Clerk as a top choice.

React framework integrations

Clerk’s React and NextJS integrations are extensive. I’m not going to do a walk-through on setting up a new React/NextJS app with Clerk because their website already offers a bunch of quick-start guides for many of the popular frameworks. Instead, we’ll just check out some of the important parts of their APIs that make it such a popular choice for React apps. They also offer support for React Native and Expo as well in case you’re planning on building a native application.

The React API consists of pre-built components that can be directly imported into your application and a hook-based API.

React Component API

Clerk provides a set of pre-built components that can be directly imported into your React or Next.js application. These components are designed to handle common authentication tasks, such as sign-up, sign-in, and password reset forms. By using these pre-built components, you can save the time and effort of creating your own custom UI components for authentication.

Some of the pre-built components include:

  • SignIn: A sign-in form that handles user authentication with email, password, or social login.
  • SignUp: A sign-up form that manages user registration with email, password, or social login.
  • UserButton: A button that displays the current user's profile picture and opens a dropdown menu with user-related actions.
  • UserMenu: A dropdown menu with user-related actions, such as signing out or account management.
  • UserProfile and OrganizationProfile: Easily display and manage user and organization information.
  • OrganizationSwitcher: Simplifies multi-tenant applications by allowing users to effortlessly switch between organizations and manage their memberships within your application.

The component API comes equipped with themes that you can use out of the box to help match your website and also allow configuration for customizing the style even further.

import { dark } from '@clerk/themes';
import { ClerkProvider, SignIn } from '@clerk/nextjs';
import type { AppProps } from "next/app";

function MyApp({ pageProps }: AppProps) {
  return (
    <ClerkProvider
      appearance={{
        baseTheme: dark
      }}
    >
      <Component {...pageProps}>
    </ClerkProvider>
  )
}

React Hooks API

Clerk's React API also includes a set of hooks that allow you to interact with the authentication system programmatically. These hooks provide a simple and intuitive way to manage user authentication state and perform actions related to user accounts.

Some of the hooks available in Clerk's React API include:

  • useClerk: Provides access to the Clerk instance, which allows you to interact with the Clerk API directly.
  • useUser: Returns the current user object, allowing you to access user data and monitor authentication state.
  • useSignUp: Provides methods for managing the sign-up process, such as creating new user accounts or handling social logins.
  • useSignIn: Offers methods for managing the sign-in process, including authenticating users with email, password, or social login.
  • useSession & useSessionList: The useSession hook provides access to the current user's active session object, allowing you to manage and monitor the user's session state within your application. In contrast, the useSessionList hook returns an array of all active sessions for the current user, enabling you to implement features like device management and session monitoring by displaying and controlling multiple sessions across different devices and browsers.
  • useOrganization & useOrganizationList: The useOrganization hook offers access to the organization object associated with the current user, enabling you to manage organization-related features and permissions, especially in multi-tenant applications. Meanwhile, the useOrganizationList hook returns an array of all organizations the current user is a member of, allowing you to implement features like organization switching, role management, and billing by managing and displaying multiple organizations or teams within your application.

By using Clerk's pre-built components and hook-based API, integrating authentication into your React or Next.js application becomes a much more straightforward process by using these higher-level APIs.

ClerkJS API

Yes, Clerk’s React Component and Hooks APIs are really awesome, but you don’t need to be using React to take advantage of Clerk’s fantastic feature set. They do have a vanilla JavaScript API that can be used to implement your authentication using the Clerk in any client-side JavaScript environment or framework. It’s pretty similar to the React Hooks API with methods for sign-in, sign-out, user profile, sessions, and organization management. So even if you are not using React, you can still easily use Clerk for a great user and developer experience.

Server-side APIs

Their base server-side API client is pretty straightforward and includes methods for managing users and organizations. There are other backend APIs and features made available through the Backend HTTP API. The Node server API specifically includes a few more of these features.

Clerk Node

The Clerk NodeJS API has methods for managing clients, user invitations, sessions, users, and organizations. It also has methods for creating emails, SMS messages, and managing Allowlists.

Clerk Backend API

There’s quite a bit in their Backend API documentation so I’m not going to go over everything, but it’s good to know that it’s available if your backend is using a language that doesn’t yet have an API, or doesn’t include certain features. They also include some beta features that are available for use in this API. SAML Connections is an example of one of them at the time of this writing.

Webhooks

Webhooks are an essential aspect of Clerk's server-side integrations, allowing developers to receive real-time updates about authentication-related events. Clerk supports a variety of webhook events, such as user creation, user updates, and session events. By configuring webhooks, we can automate processes and integrate them with other services, like sending welcome emails, triggering notifications, or updating CRM systems.

Serverless database integrations

Clerk also has integrations with several different serverless database providers like Supabase and Hasura to name a couple. This is a really nice feature if you’re using a service like this that will allow you to secure access to your data. You can easily do things like only allow the original author user to edit a post, like when your app has some sort of social feed. Another common example might be just protecting certain queries from being ran without being authenticated as a user in your application.

Conclusion

It’s clear that the team over at Clerk has worked really hard to ship a really fantastic service for user authentication with some of the best APIs and integrations you could hope to find. It is a top-tier developer experience that allows you to ship user authentication and management to your app in literal hours without compromising on user experience along the way.

They have 3 standard pricing tiers that include a very generous free tier that you can get pretty far on. If you’ve read this article and want to give it a hands-on tryout, I suggest jumping over to their quick-start guides page. There are a bunch of great guides here that quickly take your app from 0 to authenticated.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

The Future of Dates in JavaScript: Introducing Temporal cover image

The Future of Dates in JavaScript: Introducing Temporal

The Future of Dates in JavaScript: Introducing Temporal What is Temporaal? Temporal is a proposal currently at stage 3 of the TC39 process. It's expected to revolutionize how we handle dates in JavaScript, which has always been a challenging aspect of the language. But what does it mean that it's at stage 3 of the process? * The specification is complete * It has been reviewed * It's unlikely to change significantly at this point Key Features of Temporal Temporal introduces a new global object with a fresh API. Here are some important things to know about Temporal: 1. All Temporal objects are immutable 2. They're represented in local calendar systems, but can be converted 3. Time values use 24-hour clocks 4. Leap seconds aren't represented Why Do We Need Temporal? The current Date object in JavaScript has several limitations: * No support for time zones other than the user's local time and UTC * Date objects can be mutated * Unpredictable behavior * No support for calendars other than Gregorian * Daylight savings time issues While some of these have workarounds, not all can be fixed with the current Date implementation. Let's see some useful examples where Temporal will improve our lives: Some Examples Creating a day without a time zone is impossible using Date, it also adds time beyond the date. Temporal introduces PlainDate to overcome this. ` But what if we want to add timezone information? Then we have ZonedDateTime for this purpose. The timezone must be added in this case, as it also allows a lot of flexibility when creating dates. ` Temporal is very useful when manipulating and displaying the dates in different time zones. ` Let's try some more things that are currently difficult or lead to unexpected behavior using the Date object. Operations like adding days or minutes can lead to inconsistent results. However, Temporal makes these operations easier and consistent. ` Another interesting feature of Temporal is the concept of Duration, which is the difference between two time points. We can use these durations, along with dates, for arithmetic operations involving dates and times. Note that Durations are serialized using the ISO 8601 duration format ` Temporal Objects We've already seen some of the objects that Temporal exposes. Here's a more comprehensive list. * Temporal * Temporal.Duration` * Temporal.Instant * Temporal.Now * Temporal.PlainDate * Temporal.PlainDateTime * Temporal.PlainMonthDay * Temporal.PlainTime * Temporal.PlainYearMonth * Temporal.ZonedDateTime Try Temporal Today If you want to test Temporal now, there's a polyfill available. You can install it using: ` Note that this doesn't install a global Temporal object as expected in the final release, but it provides most of the Temporal implementation for testing purposes. Conclusion Working with dates in JavaScript has always been a bit of a mess. Between weird quirks in the Date object, juggling time zones, and trying to do simple things like “add a day,” it’s way too easy to introduce bugs. Temporal is finally fixing that. It gives us a clear, consistent, and powerful way to work with dates and times. If you’ve ever struggled with JavaScript dates (and who hasn’t?), Temporal is definitely worth checking out....

“Music and code have a lot in common,” freeCodeCamp’s Jessica Wilkins on what the tech community is doing right to onboard new software engineers cover image

“Music and code have a lot in common,” freeCodeCamp’s Jessica Wilkins on what the tech community is doing right to onboard new software engineers

Before she was a software developer at freeCodeCamp, Jessica Wilkins was a classically trained clarinetist performing across the country. Her days were filled with rehearsals, concerts, and teaching, and she hadn’t considered a tech career until the world changed in 2020. > “When the pandemic hit, most of my gigs were canceled,” she says. “I suddenly had time on my hands and an idea for a site I wanted to build.” That site, a tribute to Black musicians in classical and jazz music, turned into much more than a personal project. It opened the door to a whole new career where her creative instincts and curiosity could thrive just as much as they had in music. Now at freeCodeCamp, Jessica maintains and develops the very JavaScript curriculum that has helped her and millions of developers around the world. We spoke with Jessica about her advice for JavaScript learners, why musicians make great developers, and how inclusive communities are helping more women thrive in tech. Jessica’s Top 3 JavaScript Skill Picks for 2025 If you ask Jessica what it takes to succeed as a JavaScript developer in 2025, she won’t point you straight to the newest library or trend. Instead, she lists three skills that sound simple, but take real time to build: > “Learning how to ask questions and research when you get stuck. Learning how to read error messages. And having a strong foundation in the fundamentals” She says those skills don’t come from shortcuts or shiny tools. They come from building. > “Start with small projects and keep building,” she says. “Books like You Don’t Know JS help you understand the theory, but experience comes from writing and shipping code. You learn a lot by doing.” And don’t forget the people around you. > “Meetups and conferences are amazing,” she adds. “You’ll pick up things faster, get feedback, and make friends who are learning alongside you.” Why So Many Musicians End Up in Tech A musical past like Jessica’s isn’t unheard of in the JavaScript industry. In fact, she’s noticed a surprising number of musicians making the leap into software. > “I think it’s because music and code have a lot in common,” she says. “They both require creativity, pattern recognition, problem-solving… and you can really get into flow when you’re deep in either one.” That crossover between artistry and logic feels like home to people who’ve lived in both worlds. What the Tech Community Is Getting Right Jessica has seen both the challenges and the wins when it comes to supporting women in tech. > “There’s still a lot of toxicity in some corners,” she says. “But the communities that are doing it right—like Women Who Code, Women in Tech, and Virtual Coffee—create safe, supportive spaces to grow and share experiences.” She believes those spaces aren’t just helpful, but they’re essential. > “Having a network makes a huge difference, especially early in your career.” What’s Next for Jessica Wilkins? With a catalog of published articles, open-source projects under her belt, and a growing audience of devs following her journey, Jessica is just getting started. She’s still writing. Still mentoring. Still building. And still proving that creativity doesn’t stop at the orchestra pit—it just finds a new stage. Follow Jessica Wilkins on X and Linkedin to keep up with her work in tech, her musical roots, and whatever she’s building next. Sticker illustration by Jacob Ashley....

D1 SQLite: Schema, migrations and seeds cover image

D1 SQLite: Schema, migrations and seeds

I’ve written posts about some of the popular ORM’s in TypeScript and covered their pros and cons. Prisma is probably the most well known and Drizzle is a really popular up and comer. I like and use ORM’s in most of my projects but there’s also a camp of folks who believe they shouldn’t be used. I started a small Cloudflare Workers project recently and decided to try using their D1 SQLite database without adding any ORM. This is the first post in a 2 part series where we’ll explore what this experience is like using only the driver and utilities made available in the Wrangler CLI. Introduction If you’re unfamiliar with Cloudflare D1 - it’s a distributed SQLite database for the Cloudflare Workers platform. Workers are lightweight serverless functions/compute distributed across a global network. The platform includes services and API’s like D1 that provide extended capabilities to your Workers. At the time of this writing, there are only two ways to interact with a D1 database that I’m aware of. * From a Worker using the D1 Client API * D1 HTTP API In this 2 part series, we will create a simple Workers project and use the D1 Client API to build out our queries. Getting Started For this tutorial, we’ll create a simple Cloudflare Worker project and treat it like a simple node script/http server to do our experiments. The first step is initializing a new Cloudflare Worker and D1 database: ` ` We need to take our binding and add it to our project wrangler.toml configuration. Once our binding is added we can re-generate the types for our Worker project. ` We now have our DB binding added to our project Env types. Let’s add a simple query to our worker script to make sure our database is setup and working: ` Start the development server with npm run dev and access the server at http://localhost:8787 . When the page loads we should see a successful result {"1 + 1":2} . We now have a working SQLite database available. Creating a schema Since we’re not using an ORM with some kind of DSL to define a schema for our database, we’re going to do things the old fashioned way. “Schema” will just refer to the data model that we create for our database. We’ll define it using SQL and the D1 migrations utility. Let’s create a migration to define our initial schema: ` For our demo purposes we will build out a simple blog application database. It includes posts, authors, and tags to include some relational data. We need to write the SQL in our migration to create all the tables and columns that we need: ` The SQL above defines our tables, columns, and their relations with foreign keys. It also includes a join table for posts and tags to represent a many-to-many relationship. If you don’t have a lot of experience writing SQL queries it might look a little bit intimidating at first. Once you take some time to learn it it’s actually pretty nice. DataLemur is a pretty great resource for learning SQL. If you need help with a specific query, Copilot and GPT are quite good at generating SQL queries. Just make sure you take some time to try to understand them and check for any potential issues. After completing a migration script it needs to be applied: ` I added the --local flag so that we’re working against a local database for now. Typing our Schema One of the downsides of our ORMless approach is we don’t get TypeScript types out of the box. In a smaller project, I think the easiest approach is just to manage your own types. It’s not hard, you can even have GPT help if you want. If managing type definitions for your schema is not acceptable for your project or use case you can look for a code generation tool or switch to an ORM / toolset that provides types. For this example I created some basic types to map to our schema so that we can get help from the lsp when working with our queries. ` Seeding development data Outside of our migrations, we can write SQL scripts and execute them against our D1 SQLite database using wrangler. To start we can create a simple seeds/dev.sql script to load some initial development seed data into our local database. Another example might be a reset.sql that drops all of our tables so we can easily reset our database during development as we rework the schema or run other experiments. Since our database is using auto incrementing integer ID’s, we can know up front what the ID’s for the rows we are creating are since our database is initially empty. This can be a bit tricky if you’re using randomly generated ID’s. In that case you would probably want to write a script that can collect ID’s of newly created records and use them for creating related records. Here we are just passing the integer ID directly in our SQL script. As an example, we know up front that the author Alice Smith will have the id 1, Bob Johnson 2, and so on. Post_tags looks a little bit crazy since it’s just a join table. Each row is just a post_id and a tag_id. (1, 1), (1 2), etc. Here’s the code for a dev seed script: ` Here’s the code for a reset script - it’s important to remember to drop the migrations table in your reset so you can apply your migrations. ` Using the wrangler CLI we can execute our script files against our local development and remote d1 database instances. Since we have already applied our migrations to our local database, we can use our dev.sql seed script to load some data into our db. ` The Wrangler output is pretty helpful - it lets us know to add the --remote flag to run against our remote instance. We can also use execute to run commands against our database. Lets run a select to look at the data added to our posts table. ` This command should output a table showing the columns of our db and the 7 rows we added from the dev seed script. Summary Using wrangler and the Cloudflare D1 platform we’ve already gotten pretty far without an ORM or any other additional tooling. We have a simple but effective migrations system in place and some initial scripts for easily seeding and resetting our databases. There are also some other really great things built-in to the D1 platform like time travel and backups. I definitely recommend at least skimming through the documentation at some point. In the next post we will start interacting with our database and sample data using the D1 Client API....

The simplicity of deploying an MCP server on Vercel cover image

The simplicity of deploying an MCP server on Vercel

The current Model Context Protocol (MCP) spec is shifting developers toward lightweight, stateless servers that serve as tool providers for LLM agents. These MCP servers communicate over HTTP, with OAuth handled clientside. Vercel’s infrastructure makes it easy to iterate quickly and ship agentic AI tools without overhead. Example of Lightweight MCP Server Design At This Dot Labs, we built an MCP server that leverages the DocuSign Navigator API. The tools, like `get_agreements`, make a request to the DocuSign API to fetch data and then respond in an LLM-friendly way. ` Before the MCP can request anything, it needs to guide the client on how to kick off OAuth. This involves providing some MCP spec metadata API endpoints that include necessary information about where to obtain authorization tokens and what resources it can access. By understanding these details, the client can seamlessly initiate the OAuth process, ensuring secure and efficient data access. The Oauth flow begins when the user's LLM client makes a request without a valid auth token. In this case they’ll get a 401 response from our server with a WWW-Authenticate header, and then the client will leverage the metadata we exposed to discover the authorization server. Next, the OAuth flow kicks off directly with Docusign as directed by the metadata. Once the client has the token, it passes it in the Authorization header for tool requests to the API. ` This minimal set of API routes enables me to fetch Docusign Navigator data using natural language in my agent chat interface. Deployment Options I deployed this MCP server two different ways: as a Fastify backend and then by Vercel functions. Seeing how simple my Fastify MCP server was, and not really having a plan for deployment yet, I was eager to rewrite it for Vercel. The case for Vercel: * My own familiarity with Next.js API deployment * Fit for architecture * The extremely simple deployment process * Deploy previews (the eternal Vercel customer conversion feature, IMO) Previews of unfamiliar territory Did you know that the MCP spec doesn’t “just work” for use as ChatGPT tooling? Neither did I, and I had to experiment to prove out requirements that I was unfamiliar with. Part of moving fast for me was just deploying Vercel previews right out of the CLI so I could test my API as a Connector in ChatGPT. This was a great workflow for me, and invaluable for the team in code review. Stuff I’m Not Worried About Vercel’s mcp-handler package made setup effortless by abstracting away some of the complexity of implementing the MCP server. It gives you a drop-in way to define tools, setup https-streaming, and handle Oauth. By building on Vercel’s ecosystem, I can focus entirely on shipping my product without worrying about deployment, scaling, or server management. Everything just works. ` A Brief Case for MCP on Next.js Building an API without Next.js on Vercel is straightforward. Though, I’d be happy deploying this as a Next.js app, with the frontend features serving as the documentation, or the tools being a part of your website's agentic capabilities. Overall, this lowers the barrier to building any MCP you want for yourself, and I think that’s cool. Conclusion I'll avoid quoting Vercel documentation in this post. AI tooling is a critical component of this natural language UI, and we just want to ship. I declare Vercel is excellent for stateless MCP servers served over http....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co