react-turnstile: Cloudflare Turnstile as a React component with a ref
Cloudflare Turnstile integration for React.
At a glance
- What is it?
- The @marsidev/react-turnstile package wraps Cloudflare Turnstile in a typed React component with an imperative ref API, four widget sizes and automatic script injection, small enough at 6.3 KB minified, but tied to one specific verification vendor.
- Who is it for?
- react-turnstile suits React teams already using Cloudflare Turnstile who want a typed component, a ref based API for form flows, and working server side rendering without hand managing the script tag. It is the wrong choice if you need to swap verification vendors later, if you are on a framework outside the React ecosystem, or if a fixed 6.3 KB bundle matters more than the ergonomics of the ref API.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the package actually wraps
react-turnstile is a React integration for Cloudflare Turnstile, published to npm under the scoped name @marsidev/react-turnstile and written in TypeScript. The README describes it as privacy-first with no user tracking and no cookies, GDPR-friendly by design, which is a property of the Turnstile service the component embeds rather than something the component adds.
The feature list is short and specific: lightweight at only 6.3 KB minified, smart verification that is often invisible to users with minimal interaction, zero config through automatic script injection, multiple widget sizes named normal, compact, flexible and invisible, an imperative API exposed through a ref, SSR readiness with Next.js, Remix and any React SSR framework, and full TypeScript support.
That combination targets a specific problem. If you have already chosen Cloudflare Turnstile as your bot check, the remaining work is the plumbing: loading the Turnstile script, rendering the widget, extracting the token when the user passes, and resetting the widget for the next attempt. The component absorbs that plumbing so your form code deals in a token string.
Documentation lives on a docs.page site for the project and there is a playground at turnstile.marsidev.com for trying the widget without setting up a project.
The ref API is what turns the widget into form infrastructure
The component has two faces. Declaratively it renders the widget. Through a ref it exposes an imperative API with four methods, reset(), execute(), getResponse() and isExpired().
getResponse() is the one that matters most in a form submission path. It returns the token that your server must validate with Cloudflare. reset() clears the widget so a new attempt can start, and isExpired() tells you whether the current token has passed its validity window, which is the check people usually forget until a user sees a validation failure on a form they filled in correctly.
The README example wires this into a login form:
import { Turnstile } from '@marsidev/react-turnstile'
import type { TurnstileInstance } from '@marsidev/react-turnstile'
import { useRef } from 'react'
function LoginForm() {
const turnstileRef = useRef<TurnstileInstance | null>(null)
async function handleSubmit(e: FormEvent) {
e.preventDefault()
const token = turnstileRef.current?.getResponse()
// Send token to your server...
turnstileRef.current?.reset() // Reset after submission
}The type TurnstileInstance is exported, so the ref is typed rather than any. The example calls preventDefault on the submit event, sends the token to the server, and resets the widget afterwards. That last call is the pattern the component is designed around: one widget, repeated submissions, no page reload.
Installation is one command, and the key comes from Cloudflare
The getting started section has two steps. The first is to follow Cloudflare's own guide to obtain a free site key and secret key. The second is to install the package:
npm i @marsidev/react-turnstile -EThe -E flag pins the exact installed version, which matters for a package whose releases arrive frequently. The last three published versions are 1.6.2 on 2026-10-11, 1.6.1 on 2026-08-26 and 1.6.0 on 2026-08-11.
The minimal usage is two lines of component:
import { Turnstile } from '@marsidev/react-turnstile'
function Widget() {
return <Turnstile siteKey='1x00000000000000000000AA' />
}The siteKey value shown in the README is Cloudflare's own testing key, which the documentation expects you to replace with the site key from your dashboard. Note that the component takes a site key only. The secret key never reaches the browser; it stays on your server, where it is used to verify the token that getResponse() returns.
Because the widget injects the Cloudflare script itself, there is no script tag to add to your HTML, and no explicit initialisation call in your component.
SSR is a supported path, not an afterthought
The README lists SSR readiness as a feature and names Next.js, Remix, and any React SSR framework. That claim rests on the automatic script injection mentioned earlier: the component handles loading the Turnstile script itself, so a server rendered page does not need a manual script tag in the document head or a client-only wrapper to avoid a hydration mismatch.
This is the part where many widget integrations fall over. A component that assumes window exists at import time will crash during server rendering, and the usual fix is a dynamic import with ssr disabled, which in turn means your form renders later than the rest of the page. The package's own demo lives at demos/nextjs in the repository, which is the reference for how a Next.js application is expected to embed the widget.
The server side of the flow is not part of this package. The token from getResponse() has to be sent to your own endpoint and validated with the secret key there. The library covers the client widget and the token lifecycle; the verification call is yours.
What the repository looks like for contributors
The project is a pnpm workspace with changesets handling releases, and the root package.json shows the shape of the tooling. It is a private root package named root with scripts that delegate to the workspace packages.
Formatting and linting are handled by oxfmt and oxlint rather than the usual Prettier and ESLint combination: the format script runs oxfmt ., lint runs oxlint --fix ., and the check script chains lint:check, format:check and typecheck. Type checking uses the TypeScript native preview compiler.
Tests are split. The test script runs the integration tests and then the end to end tests, the end to end tests run through Playwright with a playwright.config.ts at the root, and the integration tests are scoped to the library package.
Releases go through changesets. The release script builds the library, copies LICENSE and README.md into packages/lib/, and runs changeset publish, and the version-packages script runs changeset version followed by a lockfile only install.
For local development the README says to fork or clone, install dependencies with pnpm install, and run pnpm dev to start the library with hot module replacement and the Next.js demo app concurrently. The project structure it documents is short: packages/lib for the library source, demos/nextjs for the demo app, and docs for the documentation.
Where this is the wrong choice
The first limit is vendor lock in at the component level. The component renders Cloudflare Turnstile and exposes Turnstile specific concepts. If your product later needs to change verification provider, or needs to run two providers in parallel across regions, this package is in your way in a way that a hand-rolled integration is not.
The second is that the token lifecycle still needs care. isExpired() exists because a token is not valid forever, and nothing in the component stops you from sending a stale token. A form that fails validation with an unhelpful message is the usual symptom, and the fix belongs in your submit handler rather than in the widget.
The third is the framework boundary. The package is React and TypeScript only. Teams on other frameworks, or on React with a strict bundle budget where the ref API earns nothing, will find the value is mostly the script injection, which is a small thing to do directly.
The fourth is the release cadence. Three published versions in the last two months, including a minor bump on 2026-08-11, means the interface can move under you. That is fine if you pin the version as the install command does, and awkward if you follow a floating tag.
Licence, maintenance and what to check first
The package is published under the MIT License, with the LICENSE file at the repository root and the release script copying it into the published package, so the terms travel with the artefact. MIT is permissive and imposes no copyleft obligation on code that imports the component.
The repository is not archived and the last push was on 2026-10-15, days before the 1.6.2 release on 2026-10-11, so the project is being worked on. The releases are published to npm with a scoped package name rather than through GitHub releases, and the versions are patch level for most of the recent history, which suggests bug fixes rather than API redesigns.
Contributions go through the usual route of forking and opening a pull request, or opening an issue on the repository. The repository also carries AGENTS.md and CLAUDE.md at the top level, alongside a CONTRIBUTING.md, and uses a changeset workflow so a contributor's change is described in a changeset file rather than a manual version bump.
Before you adopt it, the first thing to check is that you already have both keys from Cloudflare and that the site key matches the domain you will serve from. The second is to run the playground and the demo so you can see the four widget sizes and the invisible mode in context. The third is to decide your policy on expired tokens, because the component will hand you one and your server will reject it if you do not handle the case.
Editorial conclusion
react-turnstile suits React teams already using Cloudflare Turnstile who want a typed component, a ref based API for form flows, and working server side rendering without hand managing the script tag. It is the wrong choice if you need to swap verification vendors later, if you are on a framework outside the React ecosystem, or if a fixed 6.3 KB bundle matters more than the ergonomics of the ref API. Before adopting it, check that the site key and secret key from Cloudflare are already in place, confirm your framework handles the script injection path the way the demo does, and pin a version, with 1.6.2 published on 2026-10-11.
Frequently asked questions
How do I install and render a Cloudflare Turnstile widget in React?
Install the package with npm i @marsidev/react-turnstile -E, then render the Turnstile component with your siteKey. The README shows a basic Widget component importing Turnstile from '@marsidev/react-turnstile' and passing siteKey='1x00000000000000000000AA', which is the Cloudflare testing key.
What methods does the react-turnstile ref expose?
The imperative API exposes reset(), execute(), getResponse() and isExpired(). The type TurnstileInstance is exported from '@marsidev/react-turnstile' so a useRef can be typed against it.
Does react-turnstile work with Next.js server side rendering?
Yes, the README lists SSR readiness as a feature and names Next.js, Remix and any React SSR framework, with automatic script injection rather than a manual script tag. The repository includes a Next.js demo under demos/nextjs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/marsidev-react-turnstile)Was this useful?