[Bug]: Playwright CT & React's renderToStaticMarkup "Error: Objects are not valid as a React child (found: object with keys {__pw_type"...
Version
1.51.1
Steps to reproduce
- Clone repo here
- Run outside of PW w/
npm run start=> No issues - Run within PW test w/
npm run test=> Error
Expected behavior
No issues
Actual behavior
Error: Objects are not valid as a React child (found: object with keys {__pw_type, type, props, key}). If you meant to render a collection of children, use an array instead.
at src/build.tsx:11
9 | throw new Error("Invalid React element");
10 |
> 11 | const html = renderToStaticMarkup(element);
| ^
12 | console.log(html);
13 |
14 | return html;
at retryNode (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5981:17)
at renderNodeDestructive (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5849:11)
at finishFunctionComponent (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:4743:13)
at renderElement (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5185:11)
at retryNode (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5899:31)
at performWork (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6743:17)
at startWork (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:7467:7)
at renderToStringImpl (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:7555:7)
at process.env.NODE_ENV.exports.renderToStaticMarkup (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:9019:14)
at render (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/src/build.tsx:11:36)
at Object.<anonymous> (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/src/build.tsx:17:1)
at Object.<anonymous> (/Users/nedwilbur/Projects/pw-rendertostaticmarkup/src/renderToStaticMarkup.test.tsx:2:1)
Additional context
I understand this error is coming from React, but I suspect Playwright is adding this __pw_type property, which renderToStaticMarkup cannot handle. This is needed to validate the output of renderToStaticMarkup as the output of this is used in parts of our application.
Environment
Binaries:
Node: 22.14.0 - ~/.nvm/versions/node/v22.14.0/bin/node
Yarn: 1.22.22 - ~/.yarn/bin/yarn
npm: 10.9.2 - ~/.nvm/versions/node/v22.14.0/bin/npm
IDEs:
VSCode: 1.99.2 - /opt/homebrew/bin/code
Languages:
Bash: 5.2.37 - /opt/homebrew/bin/bash
npmPackages:
@playwright/experimental-ct-react: ^1.51.1 => 1.51.1
@playwright/test: ^1.51.1 => 1.51.1
Update - this occurs with standard PW package too, not just with the CT wrapper package.
Can you provide more context on what you're trying to accomplish? With the repro you shared, you're not actually using anything from Component Testing other than the settings (you're referencing defineConfig). Playwright has to do something with JSX it's provided with; Playwright transforms it into a shared format that is then consumed in the individual CT framework implementations. In your case, you're not using those implementations, and are instead passing JSX output directly to React.
What do you want to do with renderToStaticMarkup()? How does it relate to testing with Playwright?
@agg23 I updated the project to use vanilla playwright and the issue still occurs.
We use React for our email templates, which are "built" via renderToStaticMarkup, and eventually used when generating emails.
We want to use Playwright to visually validate emails inside various email clients via toHaveScreenshot. The test ideally would "build" the email templates via renderToStaticMarkup, send the email with the generated HTML, navigate to each email client, and visually validate it against snapshots.
As a workaround, we are "pre-building" the templates using renderToStaticMarkup to a file, then kicking off the tests which read the pre-built file. This adds a bit of complexity to our processes and would be streamlined if PW did not append this property and we could call renderToStaticMarkup within the test itself.
Playwright transpiles JSX into a shared format so that it doesn't crash whenever people import a file that contains JSX. This was later expanded for component testing, so you could actually use the JSX we transpile. You can see our JSX runtime here.
Component testing consumes this JSX in its own way. Since you're using React, see how we convert the objects into React elements.
What I believe you want to have happen is you want Playwright to automatically (or through some configured property) know that you're using React, so your in-test renderToStaticMarkup() calls actually receive the React data you expect. This is not currently a supported use case.
Your options currently are to either use the playwright-ct-react render() method to run your JSX code (this will run it in-browser, which may not even be supported by React and is a lot slower than running in Node directly) or to duplicate the React JSX handling code) to build the objects yourself to pass to renderToStaticMarkup(). You could also spawn a separate Node process or pre-generate your output templates.
A potential workaround is to use Preact and https://www.npmjs.com/package/preact-render-to-string . I am using it to do server-side rendering of JSX.
At the moment, I'm switching a project to use React and renderToString from react-dom/server. I was surprised to see all of the Playwright tests fail because apparently Playwright is modifying JSX.
GET /signup-email-page 500 6.741 ms - 5834
Error: Objects are not valid as a React child (found: object with keys {__pw_type, type, props, key}). If you meant to render a collection of children, use an array instead.
at retryNode (/app/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:5981:17)
at performWork (/app/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:6743:17)
at startWork (/app/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:7467:7)
at renderToStringImpl (/app/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:7555:7)
at process.env.NODE_ENV.exports.renderToString (/app/node_modules/react-dom/cjs/react-dom-server-legacy.node.development.js:9027:14)
at renderHtml (/app/api/tsx.tsx:10:39)
at /app/api/server.tsx:261:27
Now I'm trying to find a way to turn off the component testing functionality. I'm getting the error with v1.37.1 through v1.54.2 (the latest).
Related:
- https://github.com/microsoft/playwright/issues/26936
Haven't tested it, but the beforeMount or afterMount might be useful:
// playwright/index.tsx
import { beforeMount } from '@playwright/experimental-ct-react/hooks';
beforeMount(async ({ App, hooksConfig }) => {
if (hooksConfig.emailTemplate) {
document.getElementById('root').innerHTML = renderToString(App);
}
});
// email-template.test.tsx
import { test } from '@playwright/experimental-ct-react';
test('email template', async ({ mount }) => {
const component = await mount(EmailTemplate, {
hooksConfig: { emailTemplate = true }
});
});
I'm trying to use renderToStaticMarkup to generate an HTML report in a custom reporter, and I'm also getting the same error:
Error in reporter Error: Objects are not valid as a React child (found: object with keys {__pw_type, type, props, key}). If you meant to render a collection of children, use an array instead.
I am NOT using component testing at all
I'm hitting this in a non-component context test as well.
I have a function:
// file: util.tsx
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { EmailTemplate } from './templates';
export const renderEmail = (names: string[]): string => renderToStaticMarkup(<EmailTemplate names={names} />)
It works outside of the browser and outside of playwright test, but when used in a @playwright/test, it errors out with:
Error in reporter Error: Objects are not valid as a React child (found: object with keys {__pw_type, type, props, key}). If you meant to render a collection of children, use an array instead.
@agg23 - I suspect the issue is even in projects that are not using component tests, the PW custom jsx runtime is always being used: https://github.com/microsoft/playwright/blob/02708fac2ebc02eb68b4fa4a402667953655427e/packages/playwright/bundles/babel/src/babelBundleImpl.ts#L74