diff --git a/.changeset/react17-jsx-typing.md b/.changeset/react17-jsx-typing.md
new file mode 100644
index 000000000..b3511dec5
--- /dev/null
+++ b/.changeset/react17-jsx-typing.md
@@ -0,0 +1,5 @@
+---
+'@linaria/react': patch
+---
+
+Fix TypeScript typings for React 17 projects using the automatic JSX runtime (`jsx: react-jsx`), so `styled.*` intrinsic components don’t incorrectly require `children`.
diff --git a/packages/react/__dtslint-react17__/index.d.ts b/packages/react/__dtslint-react17__/index.d.ts
new file mode 100644
index 000000000..22aafedda
--- /dev/null
+++ b/packages/react/__dtslint-react17__/index.d.ts
@@ -0,0 +1 @@
+// dtslint expects an `index.d.ts` in the test folder.
diff --git a/packages/react/__dtslint-react17__/react17.tsx b/packages/react/__dtslint-react17__/react17.tsx
new file mode 100644
index 000000000..997fddd47
--- /dev/null
+++ b/packages/react/__dtslint-react17__/react17.tsx
@@ -0,0 +1,8 @@
+import { styled } from '..';
+
+const Button = styled.button``;
+
+// Should not require `children` for intrinsic elements on React 17 projects.
+Button({});
+;
+;
diff --git a/packages/react/__dtslint-react17__/stubs/jsx-runtime.d.ts b/packages/react/__dtslint-react17__/stubs/jsx-runtime.d.ts
new file mode 100644
index 000000000..11e8b65ad
--- /dev/null
+++ b/packages/react/__dtslint-react17__/stubs/jsx-runtime.d.ts
@@ -0,0 +1,17 @@
+declare module 'react/jsx-runtime' {
+ const Fragment: unknown;
+ function jsx(type: unknown, props: unknown, key?: unknown): unknown;
+ function jsxs(type: unknown, props: unknown, key?: unknown): unknown;
+}
+
+declare module 'react/jsx-dev-runtime' {
+ const Fragment: unknown;
+ function jsxDEV(
+ type: unknown,
+ props: unknown,
+ key: unknown,
+ isStaticChildren: boolean,
+ source: unknown,
+ self: unknown
+ ): unknown;
+}
diff --git a/packages/react/__dtslint-react17__/stubs/react17.d.ts b/packages/react/__dtslint-react17__/stubs/react17.d.ts
new file mode 100644
index 000000000..5088a5856
--- /dev/null
+++ b/packages/react/__dtslint-react17__/stubs/react17.d.ts
@@ -0,0 +1,27 @@
+// Minimal React 17-like type surface for dtslint.
+// It intentionally does NOT export a module-level `JSX` namespace.
+
+declare namespace React {
+ type ElementType = unknown;
+
+ interface CSSProperties {
+ [key: string]: unknown;
+ }
+
+ interface FunctionComponent
> {
+ (props: P & { children?: unknown }): unknown;
+ }
+
+ function createElement(...args: unknown[]): unknown;
+}
+
+export = React;
+export as namespace React;
+
+declare global {
+ namespace JSX {
+ interface IntrinsicElements {
+ button: Record;
+ }
+ }
+}
diff --git a/packages/react/__dtslint-react17__/tsconfig.eslint.json b/packages/react/__dtslint-react17__/tsconfig.eslint.json
new file mode 100644
index 000000000..fc8520e73
--- /dev/null
+++ b/packages/react/__dtslint-react17__/tsconfig.eslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./tsconfig.json"
+}
diff --git a/packages/react/__dtslint-react17__/tsconfig.json b/packages/react/__dtslint-react17__/tsconfig.json
new file mode 100644
index 000000000..b607c128c
--- /dev/null
+++ b/packages/react/__dtslint-react17__/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "lib": ["es6"],
+ "target": "ES2015",
+ "jsx": "react-jsx",
+ "noImplicitAny": true,
+ "noImplicitThis": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "types": [],
+ "noEmit": true,
+ "baseUrl": "./",
+ "paths": {
+ "react": ["./stubs/react17.d.ts"],
+ "react/jsx-runtime": ["./stubs/jsx-runtime.d.ts"],
+ "react/jsx-dev-runtime": ["./stubs/jsx-runtime.d.ts"]
+ }
+ }
+}
diff --git a/packages/react/package.json b/packages/react/package.json
index 4c30c6e02..13316d255 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -53,7 +53,7 @@
"build:declarations": "tsc --emitDeclarationOnly --outDir types",
"build:dist": "tsup --format cjs,esm",
"test": "jest --config ../../jest.config.js --rootDir .",
- "test:dts": "dtslint --localTs ../../node_modules/typescript/lib __dtslint__",
+ "test:dts": "dtslint --localTs ../../node_modules/typescript/lib __dtslint__ && dtslint --localTs ../../node_modules/typescript/lib __dtslint-react17__",
"typecheck": "tsc --noEmit --composite false",
"watch": "pnpm build:dist --watch & pnpm build:declarations --watch"
},
diff --git a/packages/react/src/styled.ts b/packages/react/src/styled.ts
index 9e6e5781d..290a7489a 100644
--- a/packages/react/src/styled.ts
+++ b/packages/react/src/styled.ts
@@ -107,7 +107,6 @@ interface IProps {
style?: Record;
}
-// React <19
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace JSX {
@@ -115,17 +114,18 @@ declare global {
}
}
-// React >=19
-declare module 'react' {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace JSX {
- interface IntrinsicElements {}
- }
-}
-
let idx = 0;
-type IntrinsicElements = React.JSX.IntrinsicElements & JSX.IntrinsicElements;
+// eslint-disable-next-line @typescript-eslint/consistent-type-imports
+type ReactIntrinsicElements = typeof import('react') extends {
+ JSX: { IntrinsicElements: infer T };
+}
+ ? T
+ : never;
+
+type IntrinsicElements = [ReactIntrinsicElements] extends [never]
+ ? JSX.IntrinsicElements
+ : ReactIntrinsicElements;
// Components with props are not allowed
function styled(
diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json
index ce7588219..df536eb34 100644
--- a/packages/react/tsconfig.json
+++ b/packages/react/tsconfig.json
@@ -7,5 +7,6 @@
"node"
],
},
+ "include": ["src"],
"references": [{ "path": "../core" }]
}