{"id":10184,"date":"2025-09-21T17:13:00","date_gmt":"2025-09-21T17:13:00","guid":{"rendered":"https:\/\/testgrid.io\/blog\/?p=10184"},"modified":"2025-10-08T17:06:01","modified_gmt":"2025-10-08T17:06:01","slug":"playwright-component-testing","status":"publish","type":"post","link":"https:\/\/testgrid.io\/blog\/playwright-component-testing\/","title":{"rendered":"A Beginner&#8217;s Guide to Component Testing with Playwright"},"content":{"rendered":"\n<p>With the growing complexity of web applications, performing <a href=\"https:\/\/www.testgrid.io\/blog\/end-to-end-testing-a-detailed-guide\/\" data-type=\"link\" data-id=\"https:\/\/www.testgrid.io\/blog\/end-to-end-testing-a-detailed-guide\/\">end-to-end testing<\/a> on them is equally complex. The traditional strategy of testing the application UI can be flaky and may require regular maintenance. A paradigm shift towards component testing in such an environment is useful.<\/p>\n\n\n\n<p>By testing components in isolation, modern web UIs can be tested with a scalable approach. Teams are adopting component-driven architectures like React, and using a component testing strategy can gain overall testing confidence and quality. Playwright comes with the perfect capability to make component testing a perfect fit in your test tech stack. In this article, we will explore how we can write component tests using Playwright.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Component Testing?<\/h2>\n\n\n\n<p>Component testing can be attributed to be the testing microcosm, which focuses on verifying individual building blocks. The end-to-end test can be flaky, but these component tests are focused, much more reliable, and fast. When components are developed keeping in mind the testability, it yields less tightly coupled code. Additionally, testing early also prevents regression issues from bumping in later in the development cycle. Playwright offers component testing with vital advantages discussed in the section below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using Playwright for Component Testing<\/h2>\n\n\n\n<p>Playwright provides true isolation for the tests. It comes with a mounting and mocking model which is reliable and allows to unit test UI components without the need of an actual browser or server. This can be combined with Playwright\u2019s WebDriver capabilities for the end-to-end tests, which provides a complete spectrum coverage. This offering can hugely increase productivity and efficiency.\u00a0<\/p>\n\n\n\n<p>With Playwright you can easily target React components to test modular UIs. Playwright also ensures that the test reliability and speed are taken care of, while the developers can focus on building rather than debugging. This helps in creating a testing strategy unified across different levels achievable. Playwright component testing makes sure that irrespective of the complexity of the front end, quality is delivered.<\/p>\n\n\n\n<p>Also Read: <a href=\"https:\/\/testgrid.io\/blog\/playwright-testing\/\" data-type=\"link\" data-id=\"https:\/\/testgrid.io\/blog\/playwright-testing\/\">Playwright Automation Framework: Getting Started Guide<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Component Testing with Playwright<\/h2>\n\n\n\n<p>We will now start our journey to write our first component test using Playwright, but before we begin we will first create a React App. This React App will be used for running the component tests against its components.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">React App Creation<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open Visual Studio Code.<\/li>\n\n\n\n<li>Open the terminal and navigate to a folder where you would want to keep the react Keep.<\/li>\n\n\n\n<li>Now, enter the command:<\/li>\n<\/ol>\n\n\n\n<p>npx create-react-app appName<\/p>\n\n\n\n<p>Once done, you will see that an app is created with the name that you gave.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"764\" height=\"636\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image5-2.png\" alt=\"React App Creation for playwright component testing\" class=\"wp-image-10185\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"618\" height=\"238\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image3-3.png\" alt=\"react component testing playwright\" class=\"wp-image-10186\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>4. Next, you can open the project you just created in Visual Studio and look at its project structure:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"337\" height=\"284\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image6-2.png\" alt=\"\" class=\"wp-image-10188\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>The different folders within our project consist of the logo, images, main HTML page, app information, styles for the application components, the main component of the app, vector images, performance vitals, index js file, etc. These files help in creating a structure for our project. Note that we will be using this default project for our test execution. In real, your applications may have other files and components as well.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">React App Launch<\/h3>\n\n\n\n<p>Now that we have created the React App we will launch it. We will use the start command to start the application. Navigate to the app location using the terminal and enter the below command:<\/p>\n\n\n\n<p><strong>npm start<\/strong><\/p>\n\n\n\n<p>Once done, you will notice that your React App is up and running.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"486\" height=\"189\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image1-3.png\" alt=\"\" class=\"wp-image-10189\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>Additionally, you will notice that a browser window opens up showing a page like below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1273\" height=\"738\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image12.png\" alt=\"\" class=\"wp-image-10196\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Installing Playwright Components<\/h3>\n\n\n\n<p>After starting the React App, the next step is to start writing the tests. Before we do so we will have to install the playwright components module. To do so, run the below command in the terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>npm init playwright@latest -- --ct<\/code><\/pre>\n\n\n\n<p>Upon execution, you will see some questions to which you can select the answer that suits your requirements. We are using Typescript &amp; React 18 in this article.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"764\" height=\"636\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image5-3.png\" alt=\"\" class=\"wp-image-10191\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>Once the Playwright Component is installed, you will notice that some folders and files are also created, which can be seen from the updated project structure.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"311\" height=\"318\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image13.png\" alt=\"\" class=\"wp-image-10192\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>The new files created are the index files, .html &amp; .tsx. One will be used to render components while testing alongside mounting the components. The other can be used to apply styles, and themes as well as inject code to the page where components will be mounted.<\/p>\n\n\n\n<p>Additionally, there is a playwright-ct configuration file, which can be used to configure the browsers, timeouts, reports, etc.<\/p>\n\n\n\n<p>After launching the React App and setting up the test framework, the next step id to write the components tests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Writing and Executing the Component Test<\/h3>\n\n\n\n<p>We will first create a <strong><em>tests<\/em><\/strong> folder under the <strong><em>src<\/em><\/strong> folder, and in it, we will create our first component test file, <strong><em>componentTest.spec.tsx<\/em><\/strong>.<\/p>\n\n\n\n<p>Unlike the regular end-to-end tests where we used .ts as the extension, we use .tsx here because the React components use the JSX syntax.<\/p>\n\n\n\n<p>Write the code below in the .tsx file we just created:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>\/\/Imports the test and expect functions from the Playwright ct-react module\nimport { test, expect } from '@playwright\/experimental-ct-react';\n\/\/Imports the App component to test from the relative ..\/App path\nimport App from '..\/App';\n\n\n\/\/Configures the viewport to a 500x500 size\ntest.use({ viewport: { width: 500, height: 500 } });\n\/\/Starts a test case named \"should work\" which will run asynchronously,\n\/\/mount function binding is destructured from test parameter\ntest('should work', async ({ mount }) =&gt; {\n\/\/Uses mount() to instantiate the &lt;App \/&gt; component in isolation\n const component = await mount(&lt;App&gt;&lt;\/App&gt;);\n \/\/Asserts that component contains expected \"Learn React\" text on it verifying basic render.\n await expect(component).toContainText('Learn React');\n});\n<\/code><\/pre>\n\n\n\n<p>Before you execute this, you will have to create a <strong><em>tsconfig.json<\/em><\/strong> file inside your project directory. In this file, you will have to enable the Javascript extension using the compilerOptions. Use the code below in the tsconfig.json file:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>{\n   \"compilerOptions\": {\n     \"jsx\": \"react-jsx\",\n     \"target\": \"ESNext\",\n     \"module\": \"commonjs\",\n     \"moduleResolution\": \"Node\",\n     \"sourceMap\": true,\n     \"strict\": true,\n     \"noImplicitAny\": false\n   }\n }\n<\/code><\/pre>\n\n\n\n<p>In this file we are trying to configure the Typescript compiler to output CommonJS modules, enable React JSX, use the node module resolution strategy and enable strict type checking for the project. Note that you can alter this file as per your requirements.<\/p>\n\n\n\n<p>The next step is to execute the component test and to do so, run the below command in the terminal:<\/p>\n\n\n\n<p><strong>npm run test-ct<\/strong><\/p>\n\n\n\n<p>You will see that the component test was executed successfully with logs as below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"680\" height=\"329\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image2-3.png\" alt=\"\" class=\"wp-image-10194\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>Additionally, you can view the HTML report for your test:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1206\" height=\"398\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image10-1.png\" alt=\"running components tests playwright\" class=\"wp-image-10187\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>Now let us add some components to our app. We will be adding a counter to the React App and to do so, create a file <strong><em>Counter.js <\/em><\/strong>under the <strong><em>src <\/em><\/strong>folder and add the code below to it:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>\/\/ Import the useState hook from React\nimport { useState } from 'react'; \n\n\n\/\/ Export a default function component called Counter\nexport default function Counter() {\n\n\n \/\/ Use the useState hook to create count state variable and\n \/\/ setCount function to update it\n const &#91;count, setCount] = useState(0); \n\n\n return (\n   &lt;div&gt;\n     &lt;button onClick={() =&gt; setCount(count + 1)}&gt;\n       Increment \n     &lt;\/button&gt;\n\n\n     &lt;span&gt;{count}&lt;\/span&gt;\n   &lt;\/div&gt;\n );\n\n\n}\n<\/code><\/pre>\n\n\n\n<p>Now, we will have to include this Counter component to our React App. To do so, open the <strong><em>App.js <\/em><\/strong>&nbsp;and update the code as below:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>import logo from '.\/logo.svg';\nimport '.\/App.css';\nimport Counter from '.\/Counter';\n\n\nfunction App() {\n return (\n   &lt;div className=\"App\"&gt;\n     &lt;Counter\/&gt;\n     &lt;header className=\"App-header\"&gt;\n       &lt;img src={logo} className=\"App-logo\" alt=\"logo\" \/&gt;\n       &lt;p&gt;\n         Edit &lt;code&gt;src\/App.js&lt;\/code&gt; and save to reload.\n       &lt;\/p&gt;\n       &lt;a\n         className=\"App-link\"\n         href=\"https:\/\/reactjs.org\"\n         target=\"_blank\"\n         rel=\"noopener noreferrer\"\n       &gt;\n         Learn React\n       &lt;\/a&gt;\n     &lt;\/header&gt;\n   &lt;\/div&gt;\n );\n}\n\n\nexport default App;\n\n<\/code><\/pre>\n\n\n\n<p>Now, open the React App by running below command in terminal:<\/p>\n\n\n\n<p><strong>npm start<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"486\" height=\"189\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image1-3.png\" alt=\"\" class=\"wp-image-10189\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>This will launch the browser, and now you will see an Increment button on top of the webpage:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1103\" height=\"683\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/image9-2.png\" alt=\"\" class=\"wp-image-10195\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>Now, we will update our test to validate this new component. Update the <strong><em>componentTest.spec.tsx<\/em><\/strong> as below:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>\/\/Imports the test and expect functions from the Playwright ct-react module\nimport { test, expect } from '@playwright\/experimental-ct-react';\n\/\/Imports the App component to test from the relative ..\/App path\nimport App from '..\/App';\n\n\n\/\/Configures the viewport to a 500x500 size\ntest.use({ viewport: { width: 500, height: 500 } });\n\n\/\/Starts a test case named \"should work\" which will run asynchronously,\n\/\/mount function binding is destructured from test parameter\ntest('should work', async ({ mount }) =&gt; {\n\/\/Uses mount() to instantiate the &lt;App \/&gt; component in isolation\n const component = await mount(&lt;App&gt;&lt;\/App&gt;);\n \/\/Asserts that component contains expected \"Learn React\" text on it verifying basic render.\n await expect(component).toContainText('Learn React');\n\n\n \/\/Searches the Increment button\n await component.getByRole('button', { name: 'Increment' }).click();\n \/\/Asserts that the increment value is visible\n expect(component.getByText('1')).toBeVisible();\n\n\n});\n<\/code><\/pre>\n\n\n\n<p>Execute the test by running command <strong><em>npm run test-ct<\/em><\/strong> and see the execution results:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"660\" height=\"319\" src=\"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2025\/01\/playwright-component.png\" alt=\"playwright component test by running command npm run test-ct \" class=\"wp-image-13525\" loading=\"lazy\" title=\"\"><\/figure>\n\n\n\n<p>And there you go, you test passed with this newly added component. Let us see how we can execute a test for this component in isolation in the text section.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Executing Component Tests in Isolation<\/h3>\n\n\n\n<p>To execute this newly added component in isolation, go to your <strong><em>componentTest.spec.tsx <\/em><\/strong>and replace the import of the app with the Counter component. Additionally, we will replace the &lt;App&gt; component with &lt;Counter&gt; as shown in the code below:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background\"><code>\/\/Imports the test and expect functions from the Playwright ct-react module\nimport { test, expect } from '@playwright\/experimental-ct-react';\n\/\/Imports the Counter component to test from the relative ..\/Counter path\nimport Counter from '..\/Counter';\n\n\n\/\/Configures the viewport to a 500x500 size\ntest.use({ viewport: { width: 500, height: 500 } });\n\/\/Starts a test case named \"should work\" which will run asynchronously,\n\/\/mount function binding is destructured from test parameter\ntest('should work', async ({ mount }) =&gt; {\n\/\/Uses mount() to instantiate the &lt;Counter \/&gt; component in isolation\n const component = await mount(&lt;Counter&gt;&lt;\/Counter&gt;);\n \/\/Asserts that the button is visible\n await expect(component.getByRole('button')).toBeVisible();\n \/\/Searches the Increment button\n await component.getByRole('button', { name: 'Increment' }).click();\n});\n<\/code><\/pre>\n\n\n\n<p>Let us now execute our test and see the results:<\/p>\n\n\n\n<p><img decoding=\"async\" width=\"500\" height=\"199\" src=\"https:\/\/lh7-us.googleusercontent.com\/vZVOjMOHKhXvaPh-2jjTRbYowZihU1C8ClaxBNannEMBlL7UCiGtUEJZirBMgdyASUNpyl7Q0AX-XvU3yQ3_7VoqrpsCdd1FHWuY5uiohyR59JR2R4L2-lZ9vnQdCeAcAeFs6IUmOe-Lzybc6vDLrIc\" loading=\"lazy\" alt=\"\" title=\"\"><\/p>\n\n\n\n<p>And there you go. You have executed a component-specific test in isolation using Playwright. You can integrate other Playwright capabilities of Visual testing to capture screenshots.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Component testing allows teams to build reliable software at scale with speed.<\/li>\n\n\n\n<li><a href=\"https:\/\/testgrid.io\/blog\/what-is-shift-left-testing-and-why-is-it-important\/\" data-type=\"URL\" data-id=\"https:\/\/testgrid.io\/blog\/what-is-shift-left-testing-and-why-is-it-important\/\">Shift-left testing<\/a> and validation of unit test components help to create a stable system.<\/li>\n\n\n\n<li>Playwright provides a robust toolset to create sustainable test automation.<\/li>\n\n\n\n<li>Components act as a common thread to allow the coexistence of modularity and velocity.<\/li>\n\n\n\n<li>Playwright Component Testing is another way to embrace enhanced coverage at the module level and lay the foundation for a scalable testing strategy.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>With the growing complexity of web applications, performing end-to-end testing on them is equally complex. The traditional strategy of testing the application UI can be flaky and may require regular maintenance. A paradigm shift towards component testing in such an environment is useful. By testing components in isolation, modern web UIs can be tested with [&hellip;]<\/p>\n","protected":false},"author":29,"featured_media":11497,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[209,579],"tags":[],"class_list":["post-10184","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-test-automation","category-guide"],"acf":[],"images":{"medium":"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/A-Beginner\u0393COs-Guide-to-Component-Testing-with-Playwright_.jpg","large":"https:\/\/testgrid.io\/blog\/wp-content\/uploads\/2023\/11\/A-Beginner\u0393COs-Guide-to-Component-Testing-with-Playwright_.jpg"},"_links":{"self":[{"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/posts\/10184","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/users\/29"}],"replies":[{"embeddable":true,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/comments?post=10184"}],"version-history":[{"count":5,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/posts\/10184\/revisions"}],"predecessor-version":[{"id":15369,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/posts\/10184\/revisions\/15369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/media\/11497"}],"wp:attachment":[{"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/media?parent=10184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/categories?post=10184"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/testgrid.io\/blog\/wp-json\/wp\/v2\/tags?post=10184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}