diff --git a/.changeset/satellite-auto-sync-codemod.md b/.changeset/satellite-auto-sync-codemod.md
new file mode 100644
index 00000000000..c87902fe554
--- /dev/null
+++ b/.changeset/satellite-auto-sync-codemod.md
@@ -0,0 +1,5 @@
+---
+'@clerk/upgrade': minor
+---
+
+Add `transform-satellite-auto-sync` codemod for Core 3 migration that adds `satelliteAutoSync: true` wherever `isSatellite` is configured
diff --git a/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-satellite-auto-sync.fixtures.js b/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-satellite-auto-sync.fixtures.js
new file mode 100644
index 00000000000..88343606e85
--- /dev/null
+++ b/packages/upgrade/src/codemods/__tests__/__fixtures__/transform-satellite-auto-sync.fixtures.js
@@ -0,0 +1,173 @@
+export const fixtures = [
+ {
+ name: 'JSX: isSatellite={true}',
+ source: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+ `,
+ output: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+ `,
+ },
+ {
+ name: 'JSX: isSatellite (boolean shorthand)',
+ source: `
+import { ClerkProvider } from '@clerk/react';
+
+export const App = () => (
+
+
+
+);
+ `,
+ output: `
+import { ClerkProvider } from '@clerk/react';
+
+export const App = () => (
+
+
+
+);
+ `,
+ },
+ {
+ name: 'JSX: isSatellite with function value',
+ source: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+ url.host === 'satellite.example.com'} domain="example.com">
+ {children}
+
+ );
+}
+ `,
+ output: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+ url.host === 'satellite.example.com'}
+ domain="example.com"
+ satelliteAutoSync={true}>
+ {children}
+
+ );
+}
+ `,
+ },
+ {
+ name: 'JSX: already has satelliteAutoSync',
+ source: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+ `,
+ noChange: true,
+ },
+ {
+ name: 'Object: isSatellite in clerkMiddleware options',
+ source: `
+import { clerkMiddleware } from '@clerk/nextjs/server';
+
+export default clerkMiddleware({
+ isSatellite: true,
+ domain: 'example.com',
+});
+ `,
+ output: `
+import { clerkMiddleware } from '@clerk/nextjs/server';
+
+export default clerkMiddleware({
+ isSatellite: true,
+ satelliteAutoSync: true,
+ domain: 'example.com'
+});
+ `,
+ },
+ {
+ name: 'Object: isSatellite in variable declaration',
+ source: `
+const options = {
+ isSatellite: true,
+ domain: 'satellite.example.com',
+};
+ `,
+ output: `
+const options = {
+ isSatellite: true,
+ satelliteAutoSync: true,
+ domain: 'satellite.example.com'
+};
+ `,
+ },
+ {
+ name: 'Object: isSatellite with function value',
+ source: `
+import { clerkMiddleware } from '@clerk/nextjs/server';
+
+export default clerkMiddleware({
+ isSatellite: (url) => url.host === 'satellite.example.com',
+ domain: 'example.com',
+});
+ `,
+ output: `
+import { clerkMiddleware } from '@clerk/nextjs/server';
+
+export default clerkMiddleware({
+ isSatellite: (url) => url.host === 'satellite.example.com',
+ satelliteAutoSync: true,
+ domain: 'example.com'
+});
+ `,
+ },
+ {
+ name: 'Object: already has satelliteAutoSync',
+ source: `
+const options = {
+ isSatellite: true,
+ satelliteAutoSync: false,
+ domain: 'example.com',
+};
+ `,
+ noChange: true,
+ },
+ {
+ name: 'No isSatellite present (no changes)',
+ source: `
+import { ClerkProvider } from '@clerk/nextjs';
+
+export function App({ children }) {
+ return (
+
+ {children}
+
+ );
+}
+ `,
+ noChange: true,
+ },
+];
diff --git a/packages/upgrade/src/codemods/__tests__/transform-satellite-auto-sync.test.js b/packages/upgrade/src/codemods/__tests__/transform-satellite-auto-sync.test.js
new file mode 100644
index 00000000000..a9be8e56e98
--- /dev/null
+++ b/packages/upgrade/src/codemods/__tests__/transform-satellite-auto-sync.test.js
@@ -0,0 +1,17 @@
+import { applyTransform } from 'jscodeshift/dist/testUtils';
+import { describe, expect, it } from 'vitest';
+
+import transformer from '../transform-satellite-auto-sync.cjs';
+import { fixtures } from './__fixtures__/transform-satellite-auto-sync.fixtures';
+
+describe('transform-satellite-auto-sync', () => {
+ it.each(fixtures)('$name', ({ source, output, noChange }) => {
+ const result = applyTransform(transformer, {}, { source });
+
+ if (noChange) {
+ expect(result).toEqual('');
+ } else {
+ expect(result).toEqual(output.trim());
+ }
+ });
+});
diff --git a/packages/upgrade/src/codemods/transform-satellite-auto-sync.cjs b/packages/upgrade/src/codemods/transform-satellite-auto-sync.cjs
new file mode 100644
index 00000000000..4deb5506cd4
--- /dev/null
+++ b/packages/upgrade/src/codemods/transform-satellite-auto-sync.cjs
@@ -0,0 +1,74 @@
+module.exports = function transformSatelliteAutoSync({ source }, { jscodeshift: j }) {
+ const root = j(source);
+ let dirty = false;
+
+ // Handle JSX attributes: →
+ root.find(j.JSXOpeningElement).forEach(path => {
+ const { attributes } = path.node;
+ if (!attributes) {
+ return;
+ }
+
+ const hasIsSatellite = attributes.some(attr => isJsxAttrNamed(attr, 'isSatellite'));
+ if (!hasIsSatellite) {
+ return;
+ }
+
+ const hasAutoSync = attributes.some(attr => isJsxAttrNamed(attr, 'satelliteAutoSync'));
+ if (hasAutoSync) {
+ return;
+ }
+
+ const autoSyncAttr = j.jsxAttribute(
+ j.jsxIdentifier('satelliteAutoSync'),
+ j.jsxExpressionContainer(j.booleanLiteral(true)),
+ );
+ attributes.push(autoSyncAttr);
+ dirty = true;
+ });
+
+ // Handle object properties: { isSatellite: true } → { isSatellite: true, satelliteAutoSync: true }
+ root.find(j.ObjectExpression).forEach(path => {
+ const { properties } = path.node;
+
+ const hasIsSatellite = properties.some(prop => isObjectPropertyNamed(prop, 'isSatellite'));
+ if (!hasIsSatellite) {
+ return;
+ }
+
+ const hasAutoSync = properties.some(prop => isObjectPropertyNamed(prop, 'satelliteAutoSync'));
+ if (hasAutoSync) {
+ return;
+ }
+
+ const isSatelliteIndex = properties.findIndex(prop => isObjectPropertyNamed(prop, 'isSatellite'));
+ const autoSyncProp = j.objectProperty(j.identifier('satelliteAutoSync'), j.booleanLiteral(true));
+ properties.splice(isSatelliteIndex + 1, 0, autoSyncProp);
+ dirty = true;
+ });
+
+ return dirty ? root.toSource() : undefined;
+};
+
+module.exports.parser = 'tsx';
+
+function isJsxAttrNamed(attribute, name) {
+ return attribute && attribute.type === 'JSXAttribute' && attribute.name && attribute.name.name === name;
+}
+
+function isObjectPropertyNamed(prop, name) {
+ if (!prop || (prop.type !== 'ObjectProperty' && prop.type !== 'Property')) {
+ return false;
+ }
+ const { key } = prop;
+ if (!key) {
+ return false;
+ }
+ if (key.type === 'Identifier') {
+ return key.name === name;
+ }
+ if (key.type === 'StringLiteral' || key.type === 'Literal') {
+ return key.value === name;
+ }
+ return false;
+}
diff --git a/packages/upgrade/src/versions/core-3/index.js b/packages/upgrade/src/versions/core-3/index.js
index 2a22f7640c5..ab4ba699da5 100644
--- a/packages/upgrade/src/versions/core-3/index.js
+++ b/packages/upgrade/src/versions/core-3/index.js
@@ -29,5 +29,6 @@ export default {
{ name: 'transform-clerk-provider-inside-body', packages: ['nextjs'] },
// Migrate @clerk/react-router/api.server → @clerk/react-router/server
{ name: 'transform-react-router-api-server', packages: ['react-router'] },
+ { name: 'transform-satellite-auto-sync', packages: ['nextjs', 'react', 'expo', 'astro', 'tanstack-react-start'] },
],
};