Repository: elastic/eui
Pull request #8356: feat(website): add Utilities > Text Truncation page
Author: login: weronikaolejniczak
Labels: name: skip-changelog
description: Use on PRs to skip changelog requirement (Don't delete - used for automation)
Created: 2025-02-27T10:58:01Z | Merged: 2025-03-07T09:33:56Z

## Summary

Relates to https://github.com/elastic/eui/issues/8190

Added the Text Truncation page to the new documentation site.

## QA

**Checklist:**

- [ ] Compare the content between the old docs and the staging.
- [ ] Verify that links redirect as expected (internally, within the same tab; externally, in a new tab).
- [ ] Verify that examples work as expected.
- [ ] Make sure the prop tables is displayed for all relevant component.


Diff:
diff --git a/packages/website/docs/components/forms/selection/combo_box.mdx b/packages/website/docs/components/forms/selection/combo_box.mdx
index 5f9914a7a4b9..10e341606d32 100644
--- a/packages/website/docs/components/forms/selection/combo_box.mdx
+++ b/packages/website/docs/components/forms/selection/combo_box.mdx
@@ -810,7 +810,7 @@ export default () => {
 
 ## Truncation
 
-By default, **EuiComboBox** truncates long option text at the end of the string. You can use `truncationProps` and almost any prop that [**EuiTextTruncate**](/docs/utilities/text-truncation) accepts to configure this behavior. This can be configured at the **EuiComboBox** level, as well as by each individual option.
+By default, **EuiComboBox** truncates long option text at the end of the string. You can use `truncationProps` and almost any prop that [**EuiTextTruncate**](../../utilities/text_truncation.mdx) accepts to configure this behavior. This can be configured at the **EuiComboBox** level, as well as by each individual option.
 
 ```tsx interactive
 import React, { useState } from 'react';
diff --git a/packages/website/docs/components/forms/selection/selectable.mdx b/packages/website/docs/components/forms/selection/selectable.mdx
index 7782cd0dd632..a5d2028aa77d 100644
--- a/packages/website/docs/components/forms/selection/selectable.mdx
+++ b/packages/website/docs/components/forms/selection/selectable.mdx
@@ -690,7 +690,7 @@ export default () => {
 
 **EuiSelectable** defaults to `listProps.textWrap="truncate"`, which truncates long option text at the end of the string.
 
-You can use `listProps.truncationProps` and almost any prop that [**EuiTextTruncate**](/docs/utilities/text-truncation) accepts to configure this behavior. This can be configured at the **EuiSelectable** level, as well as by each individual option.
+You can use `listProps.truncationProps` and almost any prop that [**EuiTextTruncate**](../../utilities/text_truncation.mdx) accepts to configure this behavior. This can be configured at the **EuiSelectable** level, as well as by each individual option.
 
 ```tsx interactive
 import React, { useState } from 'react';
diff --git a/packages/website/docs/components/utilities/text_truncation.mdx b/packages/website/docs/components/utilities/text_truncation.mdx
new file mode 100644
index 000000000000..e4990dd46d70
--- /dev/null
+++ b/packages/website/docs/components/utilities/text_truncation.mdx
@@ -0,0 +1,451 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo scope={{ FixedSizeList, faker, throttle }}>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+
+import {
+  EuiFlexGroup,
+  EuiPanel,
+  EuiText,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSwitch,
+  EuiSpacer,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  // Testing toggles
+  const [virtualization, setVirtualization] = useState(false);
+  const [throttleMs, setThrottleMs] = useState(0);
+  const [lineCount, setLineCount] = useState(100);
+
+  // Number of lines of text to render
+  const text = useMemo(() => {
+    return Array.from({ length: lineCount }, () => faker.lorem.lines(5));
+  }, [lineCount]);
+
+  // Width resize observer
+  const widthRef = useRef<HTMLDivElement | null>(null);
+
+  const [width, setWidth] = useState(200);
+
+  useEffect(() => {
+    if (!widthRef.current) return;
+
+    const onObserve = throttle((entries) => {
+      // Skipping a forEach as we're only observing one element
+      setWidth(entries[0].contentRect.width);
+    }, throttleMs);
+
+    const resizeObserver = new ResizeObserver(onObserve);
+
+    document.fonts.ready.then(() => {
+      resizeObserver.observe(widthRef.current!);
+    });
+    () => resizeObserver.disconnect();
+  }, [throttleMs]);
+
+  return (
+    <EuiText>
+      <EuiFlexGroup alignItems="center" gutterSize="xl">
+        <EuiFormRow label="Lines" display="columnCompressed">
+          <EuiFieldNumber
+            value={lineCount}
+            onChange={(e) => setLineCount(Number(e.target.value))}
+            style={{ width: 100 }}
+            compressed
+          />
+        </EuiFormRow>
+        <EuiSwitch
+          label="Toggle virtualization"
+          checked={virtualization}
+          onChange={() => setVirtualization(!virtualization)}
+        />
+        <EuiFormRow label="Resize throttle" display="columnCompressed">
+          <EuiFieldNumber
+            value={throttleMs}
+            onChange={(e) => setThrottleMs(Number(e.target.value))}
+            style={{ width: 100 }}
+            compressed
+          />
+        </EuiFormRow>
+      </EuiFlexGroup>
+      <EuiSpacer size="m" />
+      <EuiPanel
+        panelRef={widthRef}
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          max-inline-size: 100%;
+          inline-size: 600px;
+          block-size: 300px;
+        `}
+      >
+        {virtualization ? (
+          <FixedSizeList
+            width={width}
+            height={268}
+            itemCount={100}
+            itemSize={24}
+          >
+            {({ index, style }) => (
+              <EuiTextTruncate
+                style={style}
+                key={index}
+                text={text[index]}
+                truncation="middle"
+                width={width}
+              />
+            )}
+          </FixedSizeList>
+        ) : (
+          text.map((text, i) => (
+            <EuiTextTruncate
+              key={i}
+              text={text}
+              truncation="middle"
+              width={width}
+            />
+          ))
+        )}
+      </EuiPanel>
+      <EuiSpacer />
+      <p>
+        Drag the panel resize handle to test performance. Use the controls above
+        to compare the performance of different approaches.
+      </p>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+## Multi line
+
+**EuiTextBlockTruncate** allows truncating text after a set number of wrapping `lines`.
+
+Please note: This component is currently a quick shortcut for <a href="https://css-tricks.com/line-clampin/" target="_blank" rel="noopener noreferrer">**the CSS `line-clamp`**</a> property. This means that truncating at the end of the text is the default, and there are currently no plans to add JavaScript customization for this behavior.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+import { faker } from '@faker-js/faker';
+
+import { EuiPanel, EuiText, EuiTextBlockTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 50ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextBlockTruncate lines={3}>
+          {faker.lorem.lines(10)}
+        </EuiTextBlockTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+## Props
+
+import docgen from '@elastic/eui-docgen/dist/components/text_truncate';
+
+<PropTable definition={docgen.EuiTextTruncate} />
+<PropTable definition={docgen.EuiTextBlockTruncate} />
diff --git a/packages/website/package.json b/packages/website/package.json
index c545a94ae36e..952e52054d36 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -42,7 +42,8 @@
     "raw-loader": "^4.0.2",
     "react": "^18.0.0",
     "react-dom": "^18.0.0",
-    "react-element-to-jsx-string": "^15.0.0"
+    "react-element-to-jsx-string": "^15.0.0",
+    "react-window": "^1.8.11"
   },
   "devDependencies": {
     "@babel/preset-react": "^7.24.7",
@@ -51,6 +52,7 @@
     "@docusaurus/types": "^3.5.2",
     "@types/react": "^18.3.3",
     "@types/react-dom": "^18.3.0",
+    "@types/react-window": "^1",
     "cross-env": "^7.0.3",
     "typescript": "~5.5.4"
   },
diff --git a/yarn.lock b/yarn.lock
index da00c7c3d8cd..704bb5fc6473 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4783,6 +4783,7 @@ __metadata:
     "@types/lodash": "npm:^4.14.202"
     "@types/react": "npm:^18.3.3"
     "@types/react-dom": "npm:^18.3.0"
+    "@types/react-window": "npm:^1"
     chroma-js: "npm:^3.1.2"
     classnames: "npm:^2.5.1"
     clsx: "npm:^2.0.0"
@@ -4797,6 +4798,7 @@ __metadata:
     react: "npm:^18.0.0"
     react-dom: "npm:^18.0.0"
     react-element-to-jsx-string: "npm:^15.0.0"
+    react-window: "npm:^1.8.11"
     typescript: "npm:~5.5.4"
   languageName: unknown
   linkType: soft
@@ -8642,7 +8644,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/react-window@npm:^1.8.8":
+"@types/react-window@npm:^1, @types/react-window@npm:^1.8.8":
   version: 1.8.8
   resolution: "@types/react-window@npm:1.8.8"
   dependencies:
@@ -30112,6 +30114,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-window@npm:^1.8.11":
+  version: 1.8.11
+  resolution: "react-window@npm:1.8.11"
+  dependencies:
+    "@babel/runtime": "npm:^7.0.0"
+    memoize-one: "npm:>=3.1.1 <6"
+  peerDependencies:
+    react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+    react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+  checksum: 10c0/5ae8da1bc5c47d8f0a428b28a600256e2db511975573e52cb65a9b27ed1a0e5b9f7b3bee5a54fb0da93956d782c24010be434be451072f46ba5a89159d2b3944
+  languageName: node
+  linkType: hard
+
 "react@npm:^18":
   version: 18.3.1
   resolution: "react@npm:18.3.1"


Reviews:
user: login: acstll
body: Only the Performance demo needs a bit of attention, this time the links should be all fine 😁 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T11:38:35Z
user: login: weronikaolejniczak
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T13:59:58Z
user: login: weronikaolejniczak
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T15:08:29Z
user: login: acstll
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T21:34:21Z
user: login: acstll
body: :shipit: 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T21:34:37Z
user: login: acstll
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T22:20:31Z

Review comments:
diff_hunk: @@ -0,0 +1,450 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
path: packages/website/docs/components/utilities/text_truncation.mdx
user: login: acstll
body: if you toggle virtualization on, the demo breaks… `react-window` is not listed as a dependency — when testing locally I had to add it also in `packages/website/src/theme/Demo/default_scope.ts` but I'm not sure that's actually needed?

I also found another way to break the demo 🙈 (also present in the old docs) — if you toggle virtualization on and reduce the number of lines (e.g. 95), when you scroll to the bottom you'll get a `Cannot read properties of undefined (reading 'length')`
created_at: 2025-03-04T11:37:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,450 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
path: packages/website/docs/components/utilities/text_truncation.mdx
user: login: weronikaolejniczak
body: @acstll react-window is only used for very specific cases so instead of adding it to global scope, I can try adding it to the `<Demo>` component and see if that works. I'll also try to debug that `Cannot read properties of undefined (reading 'length')` error. Thanks for the catch!
created_at: 2025-03-04T13:59:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,450 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
path: packages/website/docs/components/utilities/text_truncation.mdx
user: login: weronikaolejniczak
body: @acstll seems to be working correctly now, could you check? I added `react-window` dependency, I believe that's why it was failing. I couldn't replicate the `Cannot read properties of undefined (reading 'length')` either, could you check if you can still replicate and if yes, write down the ticket in either M1 or M2?
created_at: 2025-03-05T15:08:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,450 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
path: packages/website/docs/components/utilities/text_truncation.mdx
user: login: acstll
body: > if you can still replicate and if yes, write down the ticket in either M1 or M2?

will do 🙂 
created_at: 2025-03-06T21:34:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,450 @@
+---
+slug: /utilities/text-truncation
+id: utilities_text_truncation
+---
+
+# Text truncation
+
+## Single line
+
+**EuiTextTruncate** provides customizable and size-aware single line text truncation.
+
+The four truncation styles supported are `start`, `end`, `startEnd`, and `middle`. Resize the below demo to see how different truncation styles respond to dynamic width changes.
+
+<Demo>
+```tsx interactive
+import React from 'react';
+import { css } from '@emotion/react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel
+        css={css`
+          overflow: auto;
+          resize: horizontal; /* Not all browsers support resize logical properties yet */
+          resize: inline;
+          inline-size: 35ch;
+          max-inline-size: 100%;
+        `}
+      >
+        <EuiTextTruncate
+          text="The quick brown fox jumps over the lazy dog."
+          truncation="start"
+        />
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="end"
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="startEnd"
+        />
+        <EuiTextTruncate
+          text="So it thought the dog was making a poor life choice by focusing so much on mindfulness. What if its car broke down?"
+          truncation="middle"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+:::info Accessibility and comparison to text-overflow
+
+**EuiTextTruncate** attempts to mimic the behavior of `text-overflow: ellipsis` as closely as possible, although there may be edge cases and cross-browser issues, as this is essentially <a href="https://github.com/w3c/csswg-drafts/issues/3937" target="_blank" rel="noopener noreferrer">**a browser implementation**</a> we are trying to polyfill.
+
+* Screen readers should ignore the truncated text and only read out the full text.
+
+* Sighted mouse users will be able to briefly hover over the truncated text and read the full text in a native browser title tooltip.
+
+* For mouse users, double clicking to select the truncated line should allow copying the full untruncated text.
+:::
+
+### Custom ellipsis
+
+By default, **EuiTextTruncate** uses the unicode character for horizontal ellipsis. It can be customized via the `ellipsis` prop as necessary (e.g. for specific languages, extra punctuation, etc).
+
+<Demo>
+```tsx interactive
+import React from 'react';
+
+import { EuiPanel, EuiText, EuiTextTruncate } from '@elastic/eui';
+
+export default () => {
+  return (
+    <EuiText>
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="Opinions differ as to how to render ellipses in printed material. According to The Chicago Manual of Style, it should consist of three periods, each separated from its neighbor by a non-breaking space. According to the AP Stylebook, the periods should be rendered with no space between them."
+          ellipsis=". . ."
+        />
+        <EuiTextTruncate
+          text="In some legal writing, an ellipsis is written as three asterisks, to make it obvious that text has been omitted or to signal that the omitted text extends beyond the end of the paragraph."
+          ellipsis=" ***"
+        />
+        <EuiTextTruncate
+          text="Brackets are often used to indicate that a quotation has been condensed for space, brevity or relevance."
+          truncation="middle"
+          ellipsis="[...]"
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation offset
+
+The `start` and `end` truncation types support a `truncationOffset` property that allows preserving a specified number of characters at either the start or end of the text. Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationOffset, setTruncationOffset] = useState(3);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation offset">
+        <EuiFieldNumber
+          compressed
+          value={truncationOffset}
+          onChange={(e) => setTruncationOffset(Number(e.target.value))}
+          max={30}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="start"
+          truncationOffset={truncationOffset}
+        />
+        <EuiTextTruncate
+          text="And from the fox’s perspective, life was full of hoops to jump through, low-hanging fruit to jump for, and dead car batteries to jump-start."
+          truncation="end"
+          truncationOffset={truncationOffset}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Truncation position
+
+The `startEnd` truncation type supports a `truncationPosition` property. By default, `startEnd` anchors the displayed text to the middle of the string. However, you may prefer to display a specific subsection of the full text closer to the start or end, which this prop allows.
+
+This behavior will intelligently detect when positions are near enough to the start or end of the text to omit leading or trailing ellipses when necessary.
+
+Increase or decrease the number control below to see the prop in action.
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiPanel,
+  EuiFormRow,
+  EuiFieldNumber,
+  EuiSpacer,
+  EuiText,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+export default () => {
+  const [truncationPosition, setTruncationPosition] = useState(45);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Truncation position">
+        <EuiFieldNumber
+          compressed
+          value={truncationPosition}
+          onChange={(e) => setTruncationPosition(Number(e.target.value))}
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text="But the dog wasn’t lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping."
+          truncation="startEnd"
+          truncationPosition={truncationPosition}
+        />
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Render prop
+
+By default, **EuiTextTruncate** will automatically output the calculated truncated string. You can optionally override this by passing a render prop function to `children`, which allows for more flexible text rendering.
+
+The below example demonstrates a primary use case for the render prop and the `truncationPosition` prop. If a user is searching for a specific word in truncated text, you can use [EuiHighlight](./highlight_and_mark.mdx#highlight) or [EuiMark](./highlight_and_mark.mdx#mark) to highlight the search term, and passing the index of the found word to `truncationPosition` ensures the search term is always visible to the user.
+
+:::warning
+Do not render excessive extra content alongside the text. The sizing logic will not account for it, and the truncation calculations will be off.
+:::
+
+<Demo>
+```tsx interactive
+import React, { useState } from 'react';
+
+import {
+  EuiText,
+  EuiFormRow,
+  EuiFieldText,
+  EuiSpacer,
+  EuiPanel,
+  EuiHighlight,
+  EuiMark,
+  EuiTextTruncate,
+} from '@elastic/eui';
+
+const text =
+  "But the dog wasn't lazy, it was just practicing mindfulness, so it had a greater sense of life-satisfaction than that fox with all its silly jumping.";
+
+export default () => {
+  const [highlight, setHighlight] = useState('');
+  
+  const highlightStartPosition = text
+    .toLowerCase()
+    .indexOf(highlight.toLowerCase());
+  const highlightCenterPosition =
+    highlightStartPosition + Math.floor(highlight.length / 2);
+
+  return (
+    <EuiText>
+      <EuiFormRow label="Type to highlight text">
+        <EuiFieldText
+          value={highlight}
+          onChange={(e) => setHighlight(e.target.value)}
+          placeholder={
+            'For example, try typing "lazy", "mindful", "life", or "silly"'
+          }
+        />
+      </EuiFormRow>
+      <EuiSpacer />
+      <EuiPanel css={{ inlineSize: '40ch', maxInlineSize: '100%' }}>
+        <EuiTextTruncate
+          text={text}
+          truncation="startEnd"
+          truncationPosition={highlightCenterPosition}
+        >
+          {(truncatedText) => (
+            <>
+              {truncatedText.length > highlight.length ? (
+                <EuiHighlight search={highlight}>{truncatedText}</EuiHighlight>
+              ) : (
+                // Highlight everything if the search match is greater than the visible text
+                <EuiMark>{truncatedText}</EuiMark>
+              )}
+            </>
+          )}
+        </EuiTextTruncate>
+      </EuiPanel>
+    </EuiText>
+  );
+};
+```
+</Demo>
+
+### Performance
+
+**EuiTextTruncate** uses a canvas element under the hood to manipulate text and calculate whether the text width fits within the available width. Additionally, by default, the component will include its own resize observer in order to react to width changes.
+
+These functionalities can cause performance issues if the component is rendered over hundreds of times per page, and we would strongly recommend using caution when doing so. Several escape hatches are available for performance improvements:
+
+1. Pass a `width` prop to skip initializing a resize observer for each component instance. For text within a container of the same width, we would strongly recommend applying a single resize observer to the parent container and passing that width to all child **EuiTextTruncates**. Additionally, you may want to consider <a href="https://lodash.com/docs/#throttle" target="_blank" rel="noopener noreferrer">**throttling**</a> any resize observers or width-based logic.
+
+2. Use <a href="https://github.com/bvaughn/react-window" target="_blank" rel="noopener noreferrer">**virtualization**</a> to reduce the number of rendered elements visible at any given time. For over hundreds of instances, this will generally be the most effective solution for performance or rerender issues.
+
+3. If necessary, consider pulling out the underlying `TruncationUtils` and re-using the same canvas context, as opposed to repeatedly creating new ones.
+
+<Demo>
+```tsx interactive
+import React, { useState, useEffect, useRef, useMemo } from 'react';
+import { css } from '@emotion/react';
+import { throttle } from 'lodash';
+import { faker } from '@faker-js/faker';
+import { FixedSizeList } from 'react-window';
path: packages/website/docs/components/utilities/text_truncation.mdx
user: login: acstll
body: https://github.com/elastic/eui/issues/8409
created_at: 2025-03-06T22:20:31Z
author_association: CONTRIBUTOR

PR comments:
user: login: weronikaolejniczak
created_at: 2025-03-05T18:09:17Z
body: @acstll could you re-review the PR? I had to resolve conflicts.
author_association: CONTRIBUTOR
user: login: kibanamachine
created_at: 2025-03-06T14:55:33Z
body: Preview staging links for this PR:
- Docs site: https://eui.elastic.co/pr_8356/
- Storybook: https://eui.elastic.co/pr_8356/storybook
- New docs: https://eui.elastic.co/pr_8356/new-docs
author_association: NONE
user: login: elasticmachine
created_at: 2025-03-06T14:59:18Z
body: ## :green_heart: Build Succeeded
* [Buildkite Build](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3600)
* Commit: 2c5073c823bf3370e08c954ec06c97b9070c83b7

### History
* :broken_heart: [Build elastic/eui#3596](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3596) failed 93453fa72ca01158867ca95d5e27fc785800939b
* :broken_heart: [Build elastic/eui#3594](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3594) failed 93453fa72ca01158867ca95d5e27fc785800939b
* :broken_heart: [Build elastic/eui#3593](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3593) failed 4b2cc1d82552c2e7887fd6666cf9820a203bb3f6
* :green_heart: [Build elastic/eui#3589](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3589) succeeded 002444caa00673a865b9347bfe848d99d523c0ba
* :green_heart: [Build elastic/eui#3543](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3543) succeeded db386cc9c498fa0167a80f63d6a9b68507fc6d0c

<!--buildkite-pr-comment-eui-pull-request-test-and-deploy
{"builds":[{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3600","number":3600,"commit":"2c5073c823bf3370e08c954ec06c97b9070c83b7"},{"buildStatus":{"state":"failed","success":false,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3596","number":3596,"commit":"93453fa72ca01158867ca95d5e27fc785800939b"},{"buildStatus":{"state":"failed","success":false,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3594","number":3594,"commit":"93453fa72ca01158867ca95d5e27fc785800939b"},{"buildStatus":{"state":"failed","success":false,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3593","number":3593,"commit":"4b2cc1d82552c2e7887fd6666cf9820a203bb3f6"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3589","number":3589,"commit":"002444caa00673a865b9347bfe848d99d523c0ba"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3543","number":3543,"commit":"db386cc9c498fa0167a80f63d6a9b68507fc6d0c"}],"number":3600}
buildkite-pr-comment-->
author_association: COLLABORATOR