diff --git a/package.json b/package.json
index 9ebb0d41..9b93ac54 100644
--- a/package.json
+++ b/package.json
@@ -94,7 +94,6 @@
"husky": "9.1.7",
"jest": "29.7.0",
"metro-react-native-babel-preset": "0.77.0",
- "next": "15.1.4",
"nitro-codegen": "0.21.0",
"react": "18.3.1",
"react-native": "0.76.6",
diff --git a/src/index.ts b/src/index.ts
index 41fbadd2..13110408 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,3 +4,4 @@ export type { UnistylesThemes, UnistylesBreakpoints } from './global'
export { withUnistyles, useUnistyles } from './core'
export type { UnistylesVariants } from './types'
export { Display, Hide, ScopedTheme } from './components'
+export { useServerUnistyles, hydrateServerUnistyles, getServerUnistyles, resetServerUnistyles } from './server'
diff --git a/src/server/getServerUnistyles.tsx b/src/server/getServerUnistyles.tsx
new file mode 100644
index 00000000..a467d716
--- /dev/null
+++ b/src/server/getServerUnistyles.tsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import { StyleSheet } from 'react-native'
+import { error, isServer } from '../web/utils'
+import { UnistylesWeb } from '../web'
+import { DefaultServerUnistylesSettings, type ServerUnistylesSettings } from './types'
+
+export const getServerUnistyles = ({ includeRNWStyles = true }: ServerUnistylesSettings = DefaultServerUnistylesSettings) => {
+ if (!isServer()) {
+ throw error('Server styles should only be read on the server')
+ }
+ // @ts-ignore
+ const rnwStyle: string | null = includeRNWStyles ? (StyleSheet?.getSheet().textContent ?? '') : null
+ const css = UnistylesWeb.registry.css.getStyles()
+ const state = UnistylesWeb.registry.css.getState()
+ return <>
+ {rnwStyle && }
+
+
+ >
+}
\ No newline at end of file
diff --git a/src/server/hydrateServerUnistyles.ts b/src/server/hydrateServerUnistyles.ts
new file mode 100644
index 00000000..dd1b9e9f
--- /dev/null
+++ b/src/server/hydrateServerUnistyles.ts
@@ -0,0 +1,17 @@
+import { error, isServer } from '../web/utils'
+import { UnistylesWeb } from '../web'
+
+declare global {
+ interface Window {
+ // @ts-ignore
+ __UNISTYLES_STATE__: ReturnType
+ }
+}
+
+export const hydrateServerUnistyles = () => {
+ if (isServer()) {
+ throw error('Server styles should only be hydrated on the client')
+ }
+ UnistylesWeb.registry.css.hydrate(window.__UNISTYLES_STATE__)
+ document.querySelector('#unistyles-script')?.remove()
+}
diff --git a/src/server/index.ts b/src/server/index.ts
index 3e5e5ec7..8646875b 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1 +1,5 @@
export { useServerUnistyles } from './useServerUnistyles'
+export { getServerUnistyles } from './getServerUnistyles'
+export { resetServerUnistyles } from './resetServerUnistyles'
+export { hydrateServerUnistyles } from './hydrateServerUnistyles'
+
diff --git a/src/server/resetServerUnistyles.ts b/src/server/resetServerUnistyles.ts
new file mode 100644
index 00000000..8d80bccc
--- /dev/null
+++ b/src/server/resetServerUnistyles.ts
@@ -0,0 +1,9 @@
+import { error, isServer } from '../web/utils'
+import { UnistylesWeb } from '../web'
+
+export const resetServerUnistyles = () => {
+ if (!isServer()) {
+ throw error('Server styles should only be reset on the server')
+ }
+ UnistylesWeb.registry.reset()
+}
\ No newline at end of file
diff --git a/src/server/types.ts b/src/server/types.ts
new file mode 100644
index 00000000..50bfba37
--- /dev/null
+++ b/src/server/types.ts
@@ -0,0 +1,4 @@
+export type ServerUnistylesSettings = {
+ includeRNWStyles?: boolean
+}
+export const DefaultServerUnistylesSettings = {}
diff --git a/src/server/useServerUnistyles.tsx b/src/server/useServerUnistyles.tsx
index 407b5d56..f1876f49 100644
--- a/src/server/useServerUnistyles.tsx
+++ b/src/server/useServerUnistyles.tsx
@@ -1,42 +1,22 @@
import React, { useRef } from 'react'
-import { StyleSheet } from 'react-native'
-import { useServerInsertedHTML } from 'next/navigation'
-import { UnistylesWeb } from '../web'
+import { isServer } from '../web/utils'
+import { DefaultServerUnistylesSettings, type ServerUnistylesSettings } from './types'
+import { getServerUnistyles } from './getServerUnistyles'
+import { resetServerUnistyles } from './resetServerUnistyles'
+import { hydrateServerUnistyles } from './hydrateServerUnistyles'
-declare global {
- interface Window {
- // @ts-ignore
- __UNISTYLES_STATE__: ReturnType
- }
-}
-
-export const useServerUnistyles = () => {
+export const useServerUnistyles = (settings: ServerUnistylesSettings = DefaultServerUnistylesSettings): React.ReactNode | null => {
const isServerInserted = useRef(false)
- useServerInsertedHTML(() => {
- if (!isServerInserted.current) {
- isServerInserted.current = true
-
- // @ts-ignore
- const rnwStyle = StyleSheet?.getSheet().textContent ?? ''
- const css = UnistylesWeb.registry.css.getStyles()
- const state = UnistylesWeb.registry.css.getState()
- UnistylesWeb.registry.reset()
-
- return (
- <>
-
-
-
- >
- )
- }
-
- return null
- })
+ if (isServer() && !isServerInserted.current) {
+ isServerInserted.current = true
+ const components = getServerUnistyles(settings)
+ resetServerUnistyles()
+ return components
+ }
- if (typeof window !== 'undefined') {
- UnistylesWeb.registry.css.hydrate(window.__UNISTYLES_STATE__)
- document.querySelector('#unistyles-script')?.remove()
+ if (!isServer()) {
+ hydrateServerUnistyles()
}
+ return null
}
diff --git a/src/web/state.ts b/src/web/state.ts
index 12f0f044..b2ae46f7 100644
--- a/src/web/state.ts
+++ b/src/web/state.ts
@@ -49,8 +49,10 @@ export class UnistylesState {
return
}
- if (!this.hasAdaptiveThemes && this.CSSVars) {
- document.querySelector(':root')?.classList.add(this.themeName ?? '')
+ // Ensure we have a themeName before calling this
+ // classList.add throws a "SyntaxError" DOMException if one of the arguments is an empty string.
+ if (!this.hasAdaptiveThemes && this.CSSVars && this.themeName) {
+ document.querySelector(':root')?.classList.add(this.themeName)
}
this.services.listener.initListeners()
diff --git a/src/web/utils/common.ts b/src/web/utils/common.ts
index ad3f557d..b55c5ae3 100644
--- a/src/web/utils/common.ts
+++ b/src/web/utils/common.ts
@@ -5,7 +5,7 @@ export const reduceObject = , TReducer>(
export const keyInObject = >(obj: T, key: PropertyKey): key is keyof T => key in obj
-export const isServer = () => typeof window === 'undefined'
+export const isServer = () => typeof window === 'undefined' || typeof document === 'undefined'
export const error = (message: string) => new Error(`Unistyles: ${message}`)
diff --git a/src/web/utils/unistyle.ts b/src/web/utils/unistyle.ts
index 7f7c630d..f5fcfb32 100644
--- a/src/web/utils/unistyle.ts
+++ b/src/web/utils/unistyle.ts
@@ -68,18 +68,19 @@ export const isInDocument = (element: HTMLElement) => document.body.contains(ele
export const getMediaQuery = (query: string, allBreakpoints: Array) => {
if (Object.values(Orientation).includes(query as Orientation)) {
- return `(orientation: ${query})`
+ return `@media (orientation: ${query})`
}
if (isUnistylesMq(query)) {
const { minWidth, maxWidth, minHeight, maxHeight } = parseMq(query)
- return [
+ const queries = [
minWidth ? `(min-width: ${minWidth}px)` : undefined,
maxWidth ? `(max-width: ${maxWidth}px)` : undefined,
minHeight ? `(min-height: ${minHeight}px)` : undefined,
maxHeight ? `(max-height: ${maxHeight}px)` : undefined
].filter(Boolean).join(' and ')
+ return `@media ${queries}`
}
const breakpointValue = UnistylesWeb.runtime.breakpoints[query as keyof UnistylesBreakpoints] ?? 0
diff --git a/yarn.lock b/yarn.lock
index 2078dc7e..6a4a9bb6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3464,69 +3464,6 @@ __metadata:
languageName: node
linkType: hard
-"@next/env@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/env@npm:15.1.4"
- checksum: bb26fa2184a81d0d5265962d4f162f32d5a5a19281af475ab32e13bd700b4cf32164ca4add9fd1f849f4c970bad7a7cca25ed3414e94396472a94e2d567c4600
- languageName: node
- linkType: hard
-
-"@next/swc-darwin-arm64@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-darwin-arm64@npm:15.1.4"
- conditions: os=darwin & cpu=arm64
- languageName: node
- linkType: hard
-
-"@next/swc-darwin-x64@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-darwin-x64@npm:15.1.4"
- conditions: os=darwin & cpu=x64
- languageName: node
- linkType: hard
-
-"@next/swc-linux-arm64-gnu@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-linux-arm64-gnu@npm:15.1.4"
- conditions: os=linux & cpu=arm64 & libc=glibc
- languageName: node
- linkType: hard
-
-"@next/swc-linux-arm64-musl@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-linux-arm64-musl@npm:15.1.4"
- conditions: os=linux & cpu=arm64 & libc=musl
- languageName: node
- linkType: hard
-
-"@next/swc-linux-x64-gnu@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-linux-x64-gnu@npm:15.1.4"
- conditions: os=linux & cpu=x64 & libc=glibc
- languageName: node
- linkType: hard
-
-"@next/swc-linux-x64-musl@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-linux-x64-musl@npm:15.1.4"
- conditions: os=linux & cpu=x64 & libc=musl
- languageName: node
- linkType: hard
-
-"@next/swc-win32-arm64-msvc@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-win32-arm64-msvc@npm:15.1.4"
- conditions: os=win32 & cpu=arm64
- languageName: node
- linkType: hard
-
-"@next/swc-win32-x64-msvc@npm:15.1.4":
- version: 15.1.4
- resolution: "@next/swc-win32-x64-msvc@npm:15.1.4"
- conditions: os=win32 & cpu=x64
- languageName: node
- linkType: hard
-
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -4720,22 +4657,6 @@ __metadata:
languageName: node
linkType: hard
-"@swc/counter@npm:0.1.3":
- version: 0.1.3
- resolution: "@swc/counter@npm:0.1.3"
- checksum: df8f9cfba9904d3d60f511664c70d23bb323b3a0803ec9890f60133954173047ba9bdeabce28cd70ba89ccd3fd6c71c7b0bd58be85f611e1ffbe5d5c18616598
- languageName: node
- linkType: hard
-
-"@swc/helpers@npm:0.5.15":
- version: 0.5.15
- resolution: "@swc/helpers@npm:0.5.15"
- dependencies:
- tslib: ^2.8.0
- checksum: 1a9e0dbb792b2d1e0c914d69c201dbc96af3a0e6e6e8cf5a7f7d6a5d7b0e8b762915cd4447acb6b040e2ecc1ed49822875a7239f99a2d63c96c3c3407fb6fccf
- languageName: node
- linkType: hard
-
"@szmarczak/http-timer@npm:^5.0.1":
version: 5.0.1
resolution: "@szmarczak/http-timer@npm:5.0.1"
@@ -6190,15 +6111,6 @@ __metadata:
languageName: node
linkType: hard
-"busboy@npm:1.6.0":
- version: 1.6.0
- resolution: "busboy@npm:1.6.0"
- dependencies:
- streamsearch: ^1.1.0
- checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e
- languageName: node
- linkType: hard
-
"bytes@npm:3.1.2":
version: 3.1.2
resolution: "bytes@npm:3.1.2"
@@ -6360,7 +6272,7 @@ __metadata:
languageName: node
linkType: hard
-"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001688":
+"caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001688":
version: 1.0.30001692
resolution: "caniuse-lite@npm:1.0.30001692"
checksum: 484113e3fabbe223fff0380c25c861da265a34c3f75bb5af1f254423b43e713a3c7f0c313167df52fb203f42ea68bd0df8a9e73642becfe1e9fa5734b5fc55a5
@@ -6627,7 +6539,7 @@ __metadata:
languageName: node
linkType: hard
-"client-only@npm:0.0.1, client-only@npm:^0.0.1":
+"client-only@npm:^0.0.1":
version: 0.0.1
resolution: "client-only@npm:0.0.1"
checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8
@@ -13543,7 +13455,7 @@ __metadata:
languageName: node
linkType: hard
-"nanoid@npm:3.3.8, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8":
+"nanoid@npm:3.3.8, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8":
version: 3.3.8
resolution: "nanoid@npm:3.3.8"
bin:
@@ -13617,67 +13529,6 @@ __metadata:
languageName: node
linkType: hard
-"next@npm:15.1.4":
- version: 15.1.4
- resolution: "next@npm:15.1.4"
- dependencies:
- "@next/env": 15.1.4
- "@next/swc-darwin-arm64": 15.1.4
- "@next/swc-darwin-x64": 15.1.4
- "@next/swc-linux-arm64-gnu": 15.1.4
- "@next/swc-linux-arm64-musl": 15.1.4
- "@next/swc-linux-x64-gnu": 15.1.4
- "@next/swc-linux-x64-musl": 15.1.4
- "@next/swc-win32-arm64-msvc": 15.1.4
- "@next/swc-win32-x64-msvc": 15.1.4
- "@swc/counter": 0.1.3
- "@swc/helpers": 0.5.15
- busboy: 1.6.0
- caniuse-lite: ^1.0.30001579
- postcss: 8.4.31
- sharp: ^0.33.5
- styled-jsx: 5.1.6
- peerDependencies:
- "@opentelemetry/api": ^1.1.0
- "@playwright/test": ^1.41.2
- babel-plugin-react-compiler: "*"
- react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
- react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
- sass: ^1.3.0
- dependenciesMeta:
- "@next/swc-darwin-arm64":
- optional: true
- "@next/swc-darwin-x64":
- optional: true
- "@next/swc-linux-arm64-gnu":
- optional: true
- "@next/swc-linux-arm64-musl":
- optional: true
- "@next/swc-linux-x64-gnu":
- optional: true
- "@next/swc-linux-x64-musl":
- optional: true
- "@next/swc-win32-arm64-msvc":
- optional: true
- "@next/swc-win32-x64-msvc":
- optional: true
- sharp:
- optional: true
- peerDependenciesMeta:
- "@opentelemetry/api":
- optional: true
- "@playwright/test":
- optional: true
- babel-plugin-react-compiler:
- optional: true
- sass:
- optional: true
- bin:
- next: dist/bin/next
- checksum: e9de936fe41bdddd50be283b6ce1f35ef434f0777026400a9119d91d0f99d0f7d7101599700de7502b0340c247691f98abea551c1738f158c89d4981d11fe1cf
- languageName: node
- linkType: hard
-
"nice-try@npm:^1.0.4":
version: 1.0.5
resolution: "nice-try@npm:1.0.5"
@@ -14719,17 +14570,6 @@ __metadata:
languageName: node
linkType: hard
-"postcss@npm:8.4.31":
- version: 8.4.31
- resolution: "postcss@npm:8.4.31"
- dependencies:
- nanoid: ^3.3.6
- picocolors: ^1.0.0
- source-map-js: ^1.0.2
- checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea
- languageName: node
- linkType: hard
-
"postcss@npm:^7.0.2":
version: 7.0.39
resolution: "postcss@npm:7.0.39"
@@ -15303,7 +15143,6 @@ __metadata:
husky: 9.1.7
jest: 29.7.0
metro-react-native-babel-preset: 0.77.0
- next: 15.1.4
nitro-codegen: 0.21.0
react: 18.3.1
react-native: 0.76.6
@@ -16561,7 +16400,7 @@ __metadata:
languageName: node
linkType: hard
-"sharp@npm:0.33.5, sharp@npm:^0.33.3, sharp@npm:^0.33.5":
+"sharp@npm:0.33.5, sharp@npm:^0.33.3":
version: 0.33.5
resolution: "sharp@npm:0.33.5"
dependencies:
@@ -16813,7 +16652,7 @@ __metadata:
languageName: node
linkType: hard
-"source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1":
+"source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1":
version: 1.2.1
resolution: "source-map-js@npm:1.2.1"
checksum: 4eb0cd997cdf228bc253bcaff9340afeb706176e64868ecd20efbe6efea931465f43955612346d6b7318789e5265bdc419bc7669c1cebe3db0eb255f57efa76b
@@ -17024,13 +16863,6 @@ __metadata:
languageName: node
linkType: hard
-"streamsearch@npm:^1.1.0":
- version: 1.1.0
- resolution: "streamsearch@npm:1.1.0"
- checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942
- languageName: node
- linkType: hard
-
"strict-uri-encode@npm:^2.0.0":
version: 2.0.0
resolution: "strict-uri-encode@npm:2.0.0"
@@ -17224,22 +17056,6 @@ __metadata:
languageName: node
linkType: hard
-"styled-jsx@npm:5.1.6":
- version: 5.1.6
- resolution: "styled-jsx@npm:5.1.6"
- dependencies:
- client-only: 0.0.1
- peerDependencies:
- react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
- peerDependenciesMeta:
- "@babel/core":
- optional: true
- babel-plugin-macros:
- optional: true
- checksum: 879ad68e3e81adcf4373038aaafe55f968294955593660e173fbf679204aff158c59966716a60b29af72dc88795cfb2c479b6d2c3c87b2b2d282f3e27cc66461
- languageName: node
- linkType: hard
-
"styleq@npm:^0.1.3":
version: 0.1.3
resolution: "styleq@npm:0.1.3"
@@ -17571,7 +17387,7 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0":
+"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a