diff --git a/examples/nextjs/src/app/layout.tsx b/examples/nextjs/src/app/layout.tsx
index ec8ff0f19a7..d193b551f09 100644
--- a/examples/nextjs/src/app/layout.tsx
+++ b/examples/nextjs/src/app/layout.tsx
@@ -1,5 +1,6 @@
import './global.css'
-import {BaseStyles, ThemeProvider} from '@primer/react'
+import {ThemeProvider} from '@primer/styled-react'
+import {BaseStyles} from '@primer/react'
import {StyledComponentsRegistry} from './registry'
export const metadata = {
diff --git a/examples/nextjs/src/app/page.tsx b/examples/nextjs/src/app/page.tsx
index 7b9ad9f2e0d..a9519617d97 100644
--- a/examples/nextjs/src/app/page.tsx
+++ b/examples/nextjs/src/app/page.tsx
@@ -1,5 +1,24 @@
-import {Button} from '@primer/react'
+'use client'
+
+import {Box, Button, Stack} from '@primer/react'
+import styled from 'styled-components'
+
+const StyledDiv = styled.div(({theme}) => {
+ console.log({styledTheme: theme})
+ return {
+ padding: theme.space[5],
+ backgroundColor: theme.colors.btn.primary.bg,
+ }
+})
export default function IndexPage() {
- return
+ return (
+
+
+ Hello world
+ Hello world
+
+ )
}
diff --git a/packages/styled-react/rollup.config.js b/packages/styled-react/rollup.config.js
index 8aae93d5045..743ab953efe 100644
--- a/packages/styled-react/rollup.config.js
+++ b/packages/styled-react/rollup.config.js
@@ -26,7 +26,109 @@ export default defineConfig({
extensions: ['.ts', '.tsx'],
babelHelpers: 'bundled',
}),
+
+ /**
+ * This custom rollup plugin allows us to preserve directives in source
+ * code, such as "use client", in order to support React Server Components.
+ *
+ * The source for this plugin is inspired by:
+ * https://github.com/Ephem/rollup-plugin-preserve-directives
+ */
+ {
+ name: 'preserve-directives',
+ transform(code) {
+ const ast = this.parse(code)
+ if (ast.type !== 'Program' || !ast.body) {
+ return {
+ code,
+ ast,
+ map: null,
+ }
+ }
+
+ let hasClientDirective = false
+
+ for (const node of ast.body) {
+ if (!node) {
+ continue
+ }
+
+ if (node.type !== 'ExpressionStatement') {
+ continue
+ }
+
+ if (node.directive === 'use client') {
+ hasClientDirective = true
+ break
+ }
+ }
+
+ if (hasClientDirective) {
+ return {
+ code,
+ ast,
+ map: null,
+ meta: {
+ hasClientDirective: true,
+ },
+ }
+ }
+
+ return {
+ code,
+ ast,
+ map: null,
+ }
+ },
+ renderChunk: {
+ order: 'post',
+ handler(code, chunk, options) {
+ // If `preserveModules` is not set to true, we can't be sure if the client
+ // directive corresponds to the whole chunk or just a part of it.
+ if (!options.preserveModules) {
+ return undefined
+ }
+
+ let chunkHasClientDirective = false
+
+ for (const moduleId of Object.keys(chunk.modules)) {
+ const hasClientDirective = this.getModuleInfo(moduleId)?.meta?.hasClientDirective
+ if (hasClientDirective) {
+ chunkHasClientDirective = true
+ break
+ }
+ }
+
+ if (chunkHasClientDirective) {
+ const transformed = new MagicString(code)
+ transformed.prepend(`"use client";\n`)
+ const sourcemap = transformed.generateMap({
+ includeContent: true,
+ })
+ return {
+ code: transformed.toString(),
+ map: sourcemap,
+ }
+ }
+
+ return null
+ },
+ },
+ },
],
+ onwarn(warning, defaultHandler) {
+ // Dependencies or modules may use "use client" as an indicator for React
+ // Server Components that this module should only be loaded on the client.
+ if (warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes('use client')) {
+ return
+ }
+
+ if (warning.code === 'CIRCULAR_DEPENDENCY') {
+ throw warning
+ }
+
+ defaultHandler(warning)
+ },
output: {
dir: 'dist',
format: 'esm',
diff --git a/packages/styled-react/src/components/ThemeProvider.tsx b/packages/styled-react/src/components/ThemeProvider.tsx
new file mode 100644
index 00000000000..70a172a91f2
--- /dev/null
+++ b/packages/styled-react/src/components/ThemeProvider.tsx
@@ -0,0 +1,20 @@
+import React, {type PropsWithChildren} from 'react'
+import {ThemeProvider as SCThemeProvider} from 'styled-components'
+import {
+ ThemeProvider as PRCThemeProvider,
+ type ThemeProviderProps,
+ useTheme,
+ theme as fallbackTheme,
+} from '@primer/react'
+
+export const ThemeProvider = (props: PropsWithChildren) => {
+ const {children, ...rest} = props
+ const {theme} = useTheme()
+
+ console.log({theme})
+ return (
+
+ {children}
+
+ )
+}
diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx
index 88e659b1ab6..136697450f8 100644
--- a/packages/styled-react/src/index.tsx
+++ b/packages/styled-react/src/index.tsx
@@ -1,3 +1,5 @@
+'use client'
+
export {Box, type BoxProps} from './components/Box'
export {Button} from '@primer/react'
export {Details} from '@primer/react'
@@ -8,10 +10,10 @@ export {PageLayout} from '@primer/react'
export {Select} from '@primer/react'
export {Textarea} from '@primer/react'
export {TextInput} from '@primer/react'
-export {type TextInputProps} from '@primer/react'
+// export {type TextInputProps} from '@primer/react'
// theming depends on styled-components
-export {ThemeProvider} from '@primer/react'
+export {ThemeProvider} from './components/ThemeProvider'
export {merge} from '@primer/react'
export {theme} from '@primer/react'
export {themeGet} from '@primer/react'