diff --git a/src/content/docs/blog/launchdarkly-react-sdk-openfeature-migration.md b/src/content/docs/blog/launchdarkly-react-sdk-openfeature-migration.md
new file mode 100644
index 0000000..e779eda
--- /dev/null
+++ b/src/content/docs/blog/launchdarkly-react-sdk-openfeature-migration.md
@@ -0,0 +1,322 @@
+---
+title: "LaunchDarkly React SDK Migration to OpenFeature: A Manual-Review Playbook"
+description: "FlagLint audits your LaunchDarkly React SDK migration: detect useFlags and useVariation hooks, generate before/after diffs, and enforce OpenFeature in CI."
+date: 2026-07-16
+authors:
+ - name: Krishan Sharma
+ title: Founder and maintainer of FlagLint
+ url: https://www.linkedin.com/in/krishansha/
+tags: ["launchdarkly", "openfeature", "react", "migration", "hooks"]
+---
+
+A LaunchDarkly React SDK migration is a different problem from migrating a Node.js service. When
+you run `flaglint migrate --apply` on a server-side codebase, it rewrites `ldClient.boolVariation`
+call sites automatically. React is different. The LaunchDarkly React SDK ships a hooks-based
+evaluation API — `useFlags`, `useVariation`, `useLDClient` — that is structurally coupled to
+component trees and context providers. FlagLint detects every call site and surfaces them in the
+migration plan, but the rewrites are flagged as manual review items, not auto-applied.
+
+This article is for engineers who have run FlagLint on a React codebase and are looking at a list
+of manual review items. It covers what each hook maps to in the OpenFeature React SDK, shows
+real before/after diffs for each conversion, and explains how to lock the boundary in CI once
+migration is complete.
+
+
+
+## Start with the audit
+
+Before touching any component, get the complete flag debt inventory. Run the audit command
+against your source directory:
+
+```bash
+npx flaglint@latest audit ./src/content/docs/
+```
+
+On the FlagLint docs site itself — which has no TypeScript SDK calls — the real output is:
+
+```
+- Auditing ./src/content/docs/...
+No matching files found. Check your .flaglintrc include patterns.
+```
+
+On a real React app, point the command at your source:
+
+```bash
+npx flaglint@latest audit ./src
+```
+
+The audit report lists every direct LaunchDarkly SDK call, classified by risk level and call type.
+React SDK hooks appear in the manual review section. The readiness score reflects what fraction of
+call sites are safely automatable — React hooks count against it because FlagLint cannot
+auto-rewrite them without risking context provider breakage. Use the
+[flag debt audit guide](/blog/launchdarkly-flag-debt) if you want to include an
+effort estimate alongside the inventory.
+
+To get the raw inventory without the report format, use `scan`:
+
+```bash
+npx flaglint@latest scan ./src
+```
+
+This lists every flag key, call type, and staleness signal across your codebase. The scan output
+is the input you'll work from as you convert hooks one file at a time.
+
+---
+
+## Why React hooks are manual review items
+
+FlagLint auto-applies rewrites on the server-side Node.js SDK only. The conservative boundary is
+intentional: React SDK hooks are entangled with the component tree in ways that static analysis
+cannot safely resolve.
+
+- `useFlags()` returns a dictionary of all flag values. There is no OpenFeature equivalent — the
+ conversion requires splitting one call into one `useFlag` call per flag key, which is a
+ component-level judgment.
+- `useLDClient()` exposes the raw LaunchDarkly client. A mechanical replacement with
+ `useOpenFeatureClient()` would compile, but any downstream method calls on the client
+ object (track events, custom attributes) may have no OpenFeature analog.
+- Context provider wrappers (`LDProvider`, `withLDProvider`) are bootstrap infrastructure;
+ removing them before all hook call sites are converted will cause runtime failures.
+
+The [five patterns that block automatic migration](/blog/five-patterns-that-block-migration) covers
+the server-side equivalents in detail. The React hook situation is a separate category: these
+patterns are not blocked because they are ambiguous — they are blocked because component trees
+require human judgment to restructure.
+
+---
+
+## Install the OpenFeature React SDK
+
+The OpenFeature JavaScript ecosystem splits into server-side and web (client-side) SDKs. For
+React, you need the web SDK:
+
+```bash
+npm install @openfeature/react-sdk
+```
+
+Choose a provider that wraps your existing backend. If you are keeping LaunchDarkly as the flag
+evaluation backend, the official LaunchDarkly OpenFeature web provider is the right choice:
+
+```bash
+npm install @launchdarkly/openfeature-web-provider
+```
+
+For a full list of providers and server-side setup, see the
+[OpenFeature provider setup guide](/docs/integrations/openfeature-provider).
+
+Bootstrap once at the root of the component tree:
+
+```tsx
+// app/layout.tsx (Next.js App Router) or _app.tsx
+import { OpenFeatureProvider, OpenFeature } from '@openfeature/react-sdk'
+import { LaunchDarklyWebProvider } from '@launchdarkly/openfeature-web-provider'
+
+const provider = new LaunchDarklyWebProvider(
+ process.env.NEXT_PUBLIC_LD_CLIENT_SIDE_ID!,
+ { key: currentUser.id }
+)
+
+await OpenFeature.setProviderAndWait(provider)
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return {children}
+}
+```
+
+This is the only place that retains a reference to the LaunchDarkly SDK. Every component below it
+evaluates flags through the OpenFeature API.
+
+---
+
+## Before/after: useFlags
+
+`useFlags()` returns a dictionary of every flag value in the current evaluation context. The
+OpenFeature React SDK has no equivalent — you call `useFlag` once per flag key.
+
+**Before (LaunchDarkly React SDK):**
+
+```tsx
+import { useFlags } from 'launchdarkly-react-client-sdk'
+
+function PricingPage() {
+ const { showAnnualPricing, enableDiscountBanner } = useFlags()
+
+ return (
+ <>
+ {showAnnualPricing && }
+ {enableDiscountBanner && }
+ >
+ )
+}
+```
+
+**After (OpenFeature React SDK):**
+
+```tsx
+import { useFlag } from '@openfeature/react-sdk'
+
+function PricingPage() {
+ const { value: showAnnualPricing } = useFlag('show-annual-pricing', false)
+ const { value: enableDiscountBanner } = useFlag('enable-discount-banner', false)
+
+ return (
+ <>
+ {showAnnualPricing && }
+ {enableDiscountBanner && }
+ >
+ )
+}
+```
+
+One `useFlag` call per flag key. The `flaglint scan` output gives you the full list of flag keys
+used in each file — use it to verify you haven't missed any from a `useFlags` destructure.
+
+Note the flag key format: LaunchDarkly's React SDK typically uses camelCase keys via automatic
+transformation. The OpenFeature SDK uses the raw key as stored in your flag management backend,
+which is usually kebab-case. Check your flag keys in LaunchDarkly's dashboard before converting.
+
+---
+
+## Before/after: useVariation
+
+`useVariation(flagKey, defaultValue)` maps directly to `useFlag(flagKey, defaultValue)`. The call
+signature is almost identical; only the return shape differs.
+
+**Before:**
+
+```tsx
+import { useVariation } from 'launchdarkly-react-client-sdk'
+
+function CheckoutButton() {
+ const newCheckoutEnabled = useVariation('new-checkout-flow', false)
+ return newCheckoutEnabled ? :
+}
+```
+
+**After:**
+
+```tsx
+import { useFlag } from '@openfeature/react-sdk'
+
+function CheckoutButton() {
+ const { value: newCheckoutEnabled } = useFlag('new-checkout-flow', false)
+ return newCheckoutEnabled ? :
+}
+```
+
+Destructure `value` from the result object. OpenFeature also exposes `reason`, `errorCode`, and
+`flagMetadata` from `useFlag` — useful if you want to surface evaluation details in logging or
+observability tooling.
+
+---
+
+## Before/after: useLDClient
+
+`useLDClient()` returns the raw LaunchDarkly client instance, typically used for initialization
+checks or manual `track` calls. OpenFeature's equivalent is `useOpenFeatureClient()`.
+
+**Before:**
+
+```tsx
+import { useLDClient } from 'launchdarkly-react-client-sdk'
+
+function FeatureGate({ children }: { children: React.ReactNode }) {
+ const ldClient = useLDClient()
+ if (!ldClient) return null
+ return <>{children}>
+}
+```
+
+**After:**
+
+```tsx
+import { useOpenFeatureClient } from '@openfeature/react-sdk'
+
+function FeatureGate({ children }: { children: React.ReactNode }) {
+ const client = useOpenFeatureClient()
+ if (!client) return null
+ return <>{children}>
+}
+```
+
+If you were calling `ldClient.track()` for experimentation events, check your OpenFeature
+provider's documentation — OpenFeature does not define a standard track method. LaunchDarkly's
+OpenFeature web provider may expose track via a custom domain API.
+
+---
+
+## Remove LDProvider last
+
+The LaunchDarkly React SDK wraps the app in one of two provider components — `LDProvider` or
+`withLDProvider`. These are bootstrap infrastructure, not flag evaluation call sites.
+
+Delete them **last**, after every `useFlags`, `useVariation`, and `useLDClient` call site has been
+converted and tested. Removing the provider before converting hooks will produce runtime errors as
+components attempt to access a non-existent LaunchDarkly context.
+
+Once `LDProvider` / `withLDProvider` is removed, uninstall the React SDK package:
+
+```bash
+npm uninstall launchdarkly-react-client-sdk
+```
+
+If you were using `@launchdarkly/react-client-sdk` (the renamed package), uninstall that instead.
+
+---
+
+## Enforce the boundary in CI
+
+Once the LaunchDarkly React SDK migration is complete, prevent regressions with `flaglint validate`:
+
+```bash
+npx flaglint@latest validate ./src --no-direct-launchdarkly
+```
+
+The `--no-direct-launchdarkly` flag makes the command exit non-zero if any direct LaunchDarkly
+SDK import is found — including `launchdarkly-react-client-sdk` and `@launchdarkly/react-client-sdk`.
+Add it to CI:
+
+```yaml
+# .github/workflows/flaglint.yml
+- name: Enforce OpenFeature boundary
+ run: npx flaglint@latest validate ./src --no-direct-launchdarkly
+```
+
+If your provider or bootstrap file legitimately imports the LaunchDarkly SDK, exclude it:
+
+```bash
+npx flaglint@latest validate ./src --no-direct-launchdarkly \
+ --bootstrap-exclude "src/providers/**"
+```
+
+The [GitHub Actions enforcement guide](/blog/enforce-launchdarkly-migration-github-actions) covers
+the full CI setup, including baseline mode for gradual rollout.
+
+---
+
+## Working through the migration plan
+
+Run `flaglint migrate ./src --dry-run` to see the full migration plan across both server-side and
+React call sites. Server-side calls with generated diffs are ready to apply with `--apply`. React
+SDK hooks appear in the manual review section with file paths and flag keys.
+
+A practical order:
+
+1. Run `flaglint audit ./src` to get the full inventory and readiness score.
+2. Set up `OpenFeatureProvider` at the app root with the LaunchDarkly web provider.
+3. Convert `useVariation` call sites first — they have a near-one-to-one mapping.
+4. Convert `useFlags` call sites — split each destructured key into a separate `useFlag` call.
+5. Convert `useLDClient` call sites, handling any `track` calls separately.
+6. Remove `LDProvider` / `withLDProvider` and uninstall the React SDK package.
+7. Add `flaglint validate --no-direct-launchdarkly` to CI.
+
+The [troubleshooting guide](/docs/guides/troubleshooting) covers common setup issues, including
+the `--apply skips files` problem that can arise if your OpenFeature client binding is not
+configured — relevant for any server-side call sites in the same migration batch.
+
+---
+
+The LaunchDarkly React SDK migration is the last structural step in removing front-end vendor
+lock-in. FlagLint's scan and audit commands give you the complete call-site inventory; the
+rewrites themselves are a series of small, localized changes in individual components. The boundary
+enforcement in CI ensures the work doesn't drift back.