Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Use Selenium to locate the DOM element that React rendered, wait until the expected state appears, and read the element’s visible text. In Python, the usual call is element.text. React components do not require a special Selenium API: once React has produced ordinary browser DOM, Selenium reads that DOM.

For content that arrives after navigation, wait for the text condition itself rather than merely waiting for the page to load. For an <input> or <textarea>, read the DOM value property instead of calling .text.

The basic React text extraction pattern

This complete Python example finds a stable test element, waits for it to exist, and prints its rendered text:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

# Start a driver appropriate for your browser installation.
driver = webdriver.Chrome()
driver.get("https://example.com/dashboard")

locator = (By.CSS_SELECTOR, "[data-testid='result']")
element = WebDriverWait(driver, 10).until(
    lambda d: d.find_element(*locator)
)

text = element.text
print(text)

driver.quit()

WebElement.text returns visible rendered text (the equivalent of visible innerText), including text from descendants and excluding leading and trailing whitespace. Selenium is reading what is currently in the browser DOM, not React component state or a component instance. See Selenium’s WebElement API for the binding-specific details.

data-testid is only an example. Prefer a stable identifier that reflects your application and test goal: an explicit ID, an accessible role and name, or another selector that will survive normal UI changes. Avoid selecting a generated class name that your build system may change.

Wait for React’s rendered state, not just navigation

A React page can finish its initial navigation while JavaScript is still fetching data and updating the interface. Selenium’s waiting-strategies documentation notes that readyState covers assets declared in HTML; JavaScript can subsequently add or replace the elements your test needs. An element-existence wait therefore proves only that a node exists, not that it contains its final text.

Wait for an exact value

If the final value is known, poll that value directly:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

locator = (By.CSS_SELECTOR, "[data-testid='result']")
expected = "Ready"

WebDriverWait(driver, 10).until(
    lambda d: d.find_element(*locator).text == expected
)

text = driver.find_element(*locator).text
assert text == expected

The predicate is evaluated repeatedly until it returns a truthy result or the timeout expires. Reading the element again after the wait is intentional: React may have replaced the node while rendering.

Wait for a partial value

Use a containment check when the element includes a changing prefix, count, or label:

def has_loaded_message(d):
    return "Invoice total" in d.find_element(*locator).text

WebDriverWait(driver, 15).until(has_loaded_message)
text = driver.find_element(*locator).text

Keep the condition tied to a meaningful application state. A fixed time.sleep() only guesses how long a request will take: it can slow fast runs and still fail on a slower run. Explicit waits let each step specify the state it actually needs.

Wait for non-empty text when the exact text varies

def non_empty_text(d):
    value = d.find_element(*locator).text.strip()
    return value or False

text = WebDriverWait(driver, 10).until(non_empty_text)

This returns the first non-empty value. Use it only when any non-empty value is acceptable; if a loading placeholder is possible, wait for the specific final text or for a separate “loaded” indicator.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the API for the kind of value you need

“Text value” can mean different things in a browser. Select the retrieval method that matches the target:

Target What to read Example
Heading, paragraph, span, button label, or other displayed content Visible rendered text element.text
<input> or <textarea> content entered by a user or set by the app The DOM property named value element.get_property("value")
A value intentionally stored in an HTML attribute The relevant attribute element.get_attribute("data-status")
A JavaScript DOM property that is not an attribute The relevant property element.get_property("value")
Markup rather than plain text innerHTML, only when markup is the requirement element.get_attribute("innerHTML")

For example, a React-controlled search field usually has no useful child text. Read its current value:

field = driver.find_element(By.NAME, "query")
value = field.get_property("value")
print(value)

Do not substitute innerHTML for displayed text. It returns markup, including tags, and may contain content that is not visible to the user. Selenium’s element-information guide distinguishes rendered text from attribute and property access.

Reliable locators for React components

Prefer application-owned selectors

  • Use a stable ID when the ID is part of the page’s contract.
  • Use an accessible role, name, or label when that is how a user identifies the control.
  • Use a test ID such as data-testid when the test needs a selector independent of visual styling.
  • Use a semantic CSS relationship when it is stable, such as a result region under a named section.

The best locator is the one that identifies the intended element without depending on React’s internal component names or generated CSS hashes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make sure you selected the text-bearing node

A wrapper can exist and be visible while its useful text is rendered in a sibling, portal, or nested result element. Inspect the DOM in the browser and verify that your locator resolves to the node whose descendants contain the value. If several nodes match, narrow the selector and assert the expected count or context.

Re-find elements after a rerender

React can replace a DOM node during a state update. A previously stored WebElement can then refer to an obsolete node and produce a stale-element error. Locate the element inside the wait or immediately before reading it:

def read_result(d):
    return d.find_element(*locator).text

text = WebDriverWait(driver, 10).until(
    lambda d: read_result(d) == "Ready"
)
# Locate once more for the value you will use.
text = driver.find_element(*locator).text

If a replacement can happen between the wait and the assertion, put both the lookup and the assertion in one predicate, or catch and retry the specific stale-element condition through Selenium’s wait facilities.

Diagnose an empty string or stale value

When element.text is empty, check these causes in order:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. The locator is wrong. Print the matched element’s tag, attributes, and a small innerHTML sample while debugging. Confirm it is the result node rather than a shell or loading container.
  2. The data has not rendered yet. Replace an existence wait with a wait for the expected text, a non-empty value, or an application-specific loaded state.
  3. You are in the wrong browsing context. If the element is inside an iframe, switch to that frame before locating it. Return to the parent document when finished if later steps need the outer page.
  4. The node was replaced. Re-locate it after the React update instead of reusing an old WebElement reference.
  5. The target is a form control. For an input or textarea, read value; visible text is not stored as child text.
  6. The content is hidden. Selenium’s text method is for visible rendered text. A hidden value may require the specific attribute or property that stores it, if that is genuinely what your test is meant to verify.
  7. The page has not reached the state your test assumes. A loading label, empty state, permission gate, or failed request may be the real current UI. Capture a screenshot or inspect the DOM at the timeout to distinguish these cases.

Do not mix implicit and explicit waits

Set one deliberate waiting strategy for the test. Selenium warns that mixing implicit waits with explicit waits can make the resulting delays unpredictable. Explicit waits are usually easier to reason about because each operation states the condition it requires.

Use timeout diagnostics

When a wait times out, record the current URL, the page title, and the target element’s available text or HTML. That evidence tells you whether the selector never matched, matched a loading state, or matched content that never reached the expected value. Keep the normal timeout close to the application’s expected response time and increase it only for a known slow environment; an unnecessarily large timeout hides regressions.

Language binding differences

The concept is the same in every Selenium binding, but method names differ. Python exposes the visible-text operation as element.text. Selenium’s official JavaScript API documents the corresponding method as getText():

const { Builder, By, until } = require('selenium-webdriver');

(async function () {
  const driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://example.com/dashboard');
    const locator = By.css("[data-testid='result']");
    const element = await driver.wait(
      until.elementLocated(locator),
      10000
    );
    await driver.wait(async () => (await element.getText()) === 'Ready', 10000);
    console.log(await element.getText());
  } finally {
    await driver.quit();
  }
}());

Use the current API reference for the binding and version installed in your project. The important distinction remains: rendered element text, form-control value, and attributes or properties are separate data sources.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Testing patterns that remain stable

Assert the user-visible result

If the test is meant to protect what a user sees, assert element.text after waiting for the visible state. This catches failures where a request succeeds but the component never renders the result.

Assert a form value separately

For a controlled input, set or trigger the interaction, wait until the application updates the control, and assert its value property. Do not infer the value from a nearby label or from innerHTML.

Keep waits local to the state transition

Navigate, perform the action, then wait for the specific result that action should cause. A single global sleep or a large page-load delay makes failures slower and less informative.

Account for localization

If the application supports multiple locales, an exact string assertion can vary by locale. Either fix the locale for the test environment or assert a stable, locale-independent state exposed by the page, while still reading the rendered text when the requirement is a user-facing translation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need a visual snapshot rather than a DOM text assertion, ScreenshotNeo returns a screenshot or PDF from one request. It is not a replacement for Selenium when you must inspect a React value, click controls, or make test assertions, but it can remove the browser-driver setup for visual checks and documentation.

Using the API documented at ScreenshotNeo’s documentation:

curl -G "https://api.screenshotneo.com/v1/shot" 
  -d access_key=YOUR_API_KEY 
  --data-urlencode url=https://example.com/dashboard 
  -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/dashboard"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/dashboard'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

Before capture, ScreenshotNeo can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account to try it without a card.

Frequently asked questions

Can Selenium read text from server-rendered React?

Yes. Once the server-rendered markup or the client hydration result is present in the browser DOM, Selenium uses the same element lookup and text APIs. The wait still needs to match any client-side update your page performs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does the browser show text that my selector does not return?

The visible text may belong to a different matching node, an iframe, or a later-rendered replacement. Inspect the matched element and browsing context, then wait for the specific node that owns the displayed value.

Should I normalize whitespace before asserting?

Only when whitespace is not part of the requirement. Selenium trims outer whitespace from rendered text, but spacing and line breaks inside the content can still matter to a precise assertion. Define the formatting your test intends to guarantee.

Frequently Asked Questions

Can Selenium read text from server-rendered React?

Yes. After the server-rendered or hydrated markup exists in the browser DOM, Selenium uses the same element lookup and text APIs; wait for any later client-side update your page performs.

Why does the browser show text that my selector does not return?

The visible text may belong to another matching node, an iframe, or a replacement rendered later. Inspect the matched node and browsing context, then target the element that owns the displayed value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I normalize whitespace before asserting?

Only if whitespace is outside the requirement. Define whether internal spacing and line breaks are significant before transforming the value.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.