Cypress gives you three methods to locate web elements on the page: cy.get(), cy.find(), and cy.contains(). They look similar, but they behave very differently. Use the wrong one and your test will find too many elements, the wrong element, or nothing at all.
Let's review all three and see how they work in practice.
The three Cypress locator methods at a glance
Here's the quick version:
cy.get()finds elements on the page globally. It always searches the entire DOM.cy.find()finds child elements only. It searches within a parent element.cy.contains()finds elements by text. It returns only the first match on the page.
Now let's look at each one in depth.
cy.contains(): find elements by text
cy.contains() is probably the most interesting of the three because it comes with several nuances that catch people off guard.
Basic usage
At its simplest, cy.contains() takes a text string and finds the first element on the page that contains that text.
cy.contains('Sign In')
If your page has two "Sign In" buttons, cy.contains() will match the first one in the DOM. Unlike cy.get(), which returns all matching elements, cy.contains() always gives you a single element.
Case sensitivity
cy.contains() is case sensitive. It looks for the exact text as it appears in the DOM, not as it appears visually on the page.
Why does this matter? Because CSS can transform how text looks. A button might display "SIGN IN" in all caps on the screen, but if you inspect the DOM, the actual HTML text is "Sign In" with mixed case. The CSS text-transform: uppercase property does the visual transformation.
So if you write:
// This will FAIL if the DOM text is "Sign In"cy.contains('SIGN IN')
You get: Expected to find content 'SIGN IN' but never did.
To disable case sensitivity, pass an options object:
cy.contains('sign in', { matchCase: false })
Now Cypress finds the element regardless of case. Yes, that simple :)
Partial text match
You don't need to provide the full text. cy.contains() works with partial matches too.
// Finds an element containing "Sign In"cy.contains('Sign')
If the element's text is "Sign In", searching for just "Sign" will match it. Be careful though. A short partial match can accidentally grab an element you didn't intend.
HTML text vs attribute text
This one trips up a lot of people. cy.contains() only finds HTML text nodes, not attribute values.
Say you have an input field with a placeholder:
<input placeholder="Email" />
You see "Email" on the page, but cy.contains('Email') will not find this input field. "Email" here is a placeholder attribute, not actual text content in the DOM.
Compare that with:
<label>Remember me</label>
"Remember me" is actual HTML text. cy.contains('Remember me') finds it no problem.
So if cy.contains() can't find something you can clearly see on the page, check whether that text lives inside an HTML attribute rather than as a text node.
Two arguments: locator + text
Now here's the really cool part. You can pass two arguments to cy.contains(): a CSS selector and a text string. Cypress finds the element that matches both.
cy.contains('[status="warning"]', 'Sign In')
Say you have two "Sign In" buttons on the page. One has status="primary", the other has status="warning". By combining the selector with the text, you target the exact button you need.
You can also use this pattern to find a parent container by its heading text:
cy.contains('nb-card', 'Horizontal Form')
This finds the nb-card element that contains the text "Horizontal Form". Now you have a reference to the entire form and can chain further commands to interact with elements inside it.
This two-argument pattern is one of the most useful features of cy.contains(). It lets you build precise, readable locator strategies without relying on brittle CSS selectors alone.
cy.find(): search within a parent element
cy.find() is for locating child elements within a parent. You cannot use it standalone, it must be chained from another command.
Say you already found the "Horizontal Form" container using cy.contains():
cy.contains('nb-card', 'Horizontal Form').find('button')
This finds the button element inside the Horizontal Form only. Even if the page has dozens of buttons, cy.find() it limits the search to the children of that parent element.
You can also chain cy.contains() after finding a parent to search within it by text:
cy.contains('nb-card', 'Horizontal Form').contains('Sign In')
This works because cy.contains(), when chained from a parent, searches within that parent's subtree first.
The key point: cy.find() respects the scope of the parent element. It only looks at descendants.
You can't chain the find() method from cy.. Method find() can only be chained from another Cypress command.
cy.get(): always searches globally
cy.get() is the most commonly used locator method in Cypress. It accepts standard CSS selectors and returns all matching elements from the entire page.
cy.get('button') // All buttons on the pagecy.get('#email') // Element with id "email"cy.get('.btn-primary') // All elements with class "btn-primary"
But here's what catches most beginners. Even if you chain cy.get() from a parent element, it still searches the entire page.
// You might expect this to find buttons only inside the formcy.contains('nb-card', 'Horizontal Form').get('button')// But cy.get() ignores the parent and finds ALL buttons on the page
If the page has 8 buttons, chaining cy.get('button') from a parent container still returns all 8. The parent context is completely ignored.
If you need scoped search, use cy.find() instead.
TIP The only way to make
cy.get()respect a parent boundary is the.within()command:cy.get('form').within(() => { cy.get('input') }).
Cypress get vs find vs contains: quick comparison
So what do we have so far? Here's the summary of all three methods side by side:
Feature |
|
|
|
|---|---|---|---|
Search scope | Entire page (global) | Child elements only | First text match on page |
Locator type | CSS selectors | CSS selectors | Text (or selector + text) |
Returns | All matching elements | All matching children | Single element (first match) |
Can be standalone? | Yes | No (must chain) | Yes |
Case sensitive? | N/A | N/A | Yes (configurable) |
Partial match? | No | No | Yes |
So when do you use which? If you have a stable CSS selector and need to find elements anywhere on the page, go with cy.get(). If you already have a parent element and need to drill down into its children, cy.find() is the right pick. And if text content is the most reliable way to identify the element, cy.contains() has you covered.
Final Thoughts
Once you understand the scope rules, Cypress locator methods are pretty straightforward. cy.get() is global. cy.find() is scoped to children. cy.contains() matches by text with a few quirks you need to know.
The biggest gotcha? Chaining cy.get() from a parent and expecting scoped results. Now you know better. Use cy.find() for scoped searches, and reach for cy.contains() with two arguments when you need precise targeting without complex selectors.
If you're working with Cypress and considering what the right framework for your project is, check out this detailed comparison of Playwright vs Cypress to make an informed decision.
Cypress is a wonderful framework. In my opinion, it is the easiest UI automation scripting framework on the market. Get new skills at Bondar Academy with the Cypress UI Testing Mastery program. Start from scratch and become an expert to increase your value on the market!
Frequently Asked Questions
What is the difference between cy.get() and cy.find() in Cypress?
cy.get() always searches the entire page globally, regardless of chaining. cy.find() only searches within the children of the parent element it is chained from. Use cy.find() when you need scoped element selection.
Is cy.contains() case sensitive?
Yes, cy.contains() is case sensitive by default. It matches text exactly as it appears in the DOM. You can disable this by passing { matchCase: false } as an options argument.
Can cy.contains() find HTML attribute values?
No. cy.contains() only finds actual HTML text nodes in the DOM. Attribute values like placeholder, value, or title are not matched. Use cy.get('[placeholder="Email"]') to target elements by attribute.
How do I use cy.contains() with two arguments?
Pass a CSS selector as the first argument and the text as the second: cy.contains('.my-class', 'Button Text'). This finds an element matching both the selector and the text content.
Why does cy.get() ignore the parent when chained?
By design, cy.get() always queries from the document root. Chaining it from a parent does not limit its scope. If you need scoped queries, use cy.find() or wrap the parent with .within().
Can cy.contains() match partial text?
Yes. If an element's text is "Sign In", using cy.contains('Sign') will match it. Be cautious with very short partial strings as they may match unintended elements.
