[GH-ISSUE #435] feature: default flexShrink=0 for text elements #880

Open
opened 2026-03-14 08:54:49 +03:00 by kerem · 2 comments
Owner

Originally created by @remorses on GitHub (Dec 21, 2025).
Original GitHub issue: https://github.com/anomalyco/opentui/issues/435

When multiple text elements overflow a fixed-height box, the text becomes scrambled with characters overlapping. This happens because flexbox shrinks text elements to fit, causing them to render on top of each other.

Example without flexShrink={0}:

<box flexDirection="column" height={10} border>
  <text>Line 1: Short text</text>
  <text>Line 2: Another line</text>
  <text fg="red">Line 3: Red text</text>
  <text fg="green">Line 4: Green text</text>
  <text>Line 5: More content</text>
  <!-- more lines that overflow -->
</box>

Result - scrambled text with characters from different lines overlapping:

┌────────────────────────────────────────┐
│Line 2: Anothereshort line              │
│Line 5: GreenocoloredeteXtf the box     │
│Liner7: Bluetextchere it is quite long  │
│Line 12:lAlmostpatnthe end              │
│Line 15: Definitelysoverflowinglnow     │
└────────────────────────────────────────┘

Adding flexShrink={0} fixes it:

<box flexDirection="column" height={10} border>
  <text flexShrink={0}>Line 1: Short text</text>
  <text flexShrink={0}>Line 2: Another line</text>
  <!-- ... -->
</box>

Result - clean text that overflows correctly:

┌────────────────────────────────────────┐
│Line 1: Short text                      │
│Line 2: Another line                    │
│Line 3: Red text                        │
│Line 4: Green text                      │
│Line 5: More content                    │
└────────────────────────────────────────┘
 Line 6: Overflow renders outside box
Full reproduction code
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"

function App() {
  return (
    <box flexDirection="column" height={15} border title="Overflow Text Example">
      <text>Line 1: Short text</text>
      <text>Line 2: Another short line</text>
      <text>
        Line 3: This is a much longer line of text that should wrap to the next line when it reaches the edge of the
        box container
      </text>
      <text fg="red">Line 4: Red colored text</text>
      <text fg="green">Line 5: Green colored text</text>
      <text>
        Line 6: Another wrapping line with lots of content that will definitely need to wrap around because it is quite
        long and verbose
      </text>
      <text fg="blue">Line 7: Blue text here</text>
      <text>Line 8: Regular text</text>
      <text fg="yellow">Line 9: Yellow warning style</text>
      <text>Line 10: More content</text>
      <text>Line 11: This wrapping text contains styled parts</text>
      <text>Line 12: Almost at the end</text>
      <text fg="brightRed">Line 13: Bright red text</text>
      <text>Line 14: This line should overflow</text>
      <text>Line 15: Definitely overflowing now</text>
      <text>Line 16: Way past the box height</text>
      <text>Line 17: Still going</text>
      <text fg="brightGreen">Line 18: Bright green at the bottom</text>
      <text>Line 19: Final line of overflow content</text>
      <text>Line 20: The very last line</text>
    </box>
  )
}

const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)

flexShrink={0} should be the default for text elements:

  1. Current behavior produces garbled output that looks like a rendering bug
  2. Text shrinking vertically to fit a container doesn't make sense - text should wrap, truncate, or overflow
  3. Users who want shrinking behavior can wrap text in a box
  4. Matches browser behavior - in CSS, text nodes don't shrink to fit their container, they overflow

Related to #96. I can open a PR for this.

Originally created by @remorses on GitHub (Dec 21, 2025). Original GitHub issue: https://github.com/anomalyco/opentui/issues/435 When multiple text elements overflow a fixed-height box, the text becomes scrambled with characters overlapping. This happens because flexbox shrinks text elements to fit, causing them to render on top of each other. Example without `flexShrink={0}`: ```tsx <box flexDirection="column" height={10} border> <text>Line 1: Short text</text> <text>Line 2: Another line</text> <text fg="red">Line 3: Red text</text> <text fg="green">Line 4: Green text</text> <text>Line 5: More content</text> <!-- more lines that overflow --> </box> ``` Result - scrambled text with characters from different lines overlapping: ``` ┌────────────────────────────────────────┐ │Line 2: Anothereshort line │ │Line 5: GreenocoloredeteXtf the box │ │Liner7: Bluetextchere it is quite long │ │Line 12:lAlmostpatnthe end │ │Line 15: Definitelysoverflowinglnow │ └────────────────────────────────────────┘ ``` Adding `flexShrink={0}` fixes it: ```tsx <box flexDirection="column" height={10} border> <text flexShrink={0}>Line 1: Short text</text> <text flexShrink={0}>Line 2: Another line</text> <!-- ... --> </box> ``` Result - clean text that overflows correctly: ``` ┌────────────────────────────────────────┐ │Line 1: Short text │ │Line 2: Another line │ │Line 3: Red text │ │Line 4: Green text │ │Line 5: More content │ └────────────────────────────────────────┘ Line 6: Overflow renders outside box ``` <details> <summary>Full reproduction code</summary> ```tsx import { createCliRenderer } from "@opentui/core" import { createRoot } from "@opentui/react" function App() { return ( <box flexDirection="column" height={15} border title="Overflow Text Example"> <text>Line 1: Short text</text> <text>Line 2: Another short line</text> <text> Line 3: This is a much longer line of text that should wrap to the next line when it reaches the edge of the box container </text> <text fg="red">Line 4: Red colored text</text> <text fg="green">Line 5: Green colored text</text> <text> Line 6: Another wrapping line with lots of content that will definitely need to wrap around because it is quite long and verbose </text> <text fg="blue">Line 7: Blue text here</text> <text>Line 8: Regular text</text> <text fg="yellow">Line 9: Yellow warning style</text> <text>Line 10: More content</text> <text>Line 11: This wrapping text contains styled parts</text> <text>Line 12: Almost at the end</text> <text fg="brightRed">Line 13: Bright red text</text> <text>Line 14: This line should overflow</text> <text>Line 15: Definitely overflowing now</text> <text>Line 16: Way past the box height</text> <text>Line 17: Still going</text> <text fg="brightGreen">Line 18: Bright green at the bottom</text> <text>Line 19: Final line of overflow content</text> <text>Line 20: The very last line</text> </box> ) } const renderer = await createCliRenderer() createRoot(renderer).render(<App />) ``` </details> `flexShrink={0}` should be the default for text elements: 1. Current behavior produces garbled output that looks like a rendering bug 2. Text shrinking vertically to fit a container doesn't make sense - text should wrap, truncate, or overflow 3. Users who want shrinking behavior can wrap text in a box 4. Matches browser behavior - in CSS, text nodes don't shrink to fit their container, they overflow Related to #96. I can open a PR for this.
Author
Owner

@kommander commented on GitHub (Dec 22, 2025):

There was an explicit reason to do this, because otherwise there was weird layout behaviour for other cases 🤔

<!-- gh-comment-id:3684177786 --> @kommander commented on GitHub (Dec 22, 2025): There was an explicit reason to do this, because otherwise there was weird layout behaviour for other cases 🤔
Author
Owner

@remorses commented on GitHub (Dec 22, 2025):

Are you sure? I think adding flexShrink zero to box was the issue, not text.

<!-- gh-comment-id:3684273459 --> @remorses commented on GitHub (Dec 22, 2025): Are you sure? I think adding flexShrink zero to box was the issue, not text.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/opentui#880
No description provided.