Skip to content

[Fix] Fix st.text_area height="content" not working#14228

Merged
lukasmasuch merged 4 commits intodevelopfrom
fix/text-area-content-height
Mar 6, 2026
Merged

[Fix] Fix st.text_area height="content" not working#14228
lukasmasuch merged 4 commits intodevelopfrom
fix/text-area-content-height

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Mar 4, 2026

Describe your changes

Fixes regression in st.text_area with height="content" introduced in v1.55.0 (#14222).

  • Added uiValue to useTextInputAutoExpand dependencies so height recalculates when default value is set

Root cause: PR #13901 removed element.default fallback from getStateFromWidgetMgr. The textarea starts empty when useLayoutEffect calculates auto-expand height; the default value arrives later via useEffect but height was never recalculated.

GitHub Issue

Fixes #14222

Testing Plan

  • Unit Tests (frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx — 28 tests pass)
  • E2E Tests (e2e_playwright/st_text_area_test.py::test_text_area_content_height_expansion — passes)
  • E2E Tests (e2e_playwright/st_text_area_test.py::test_text_area_content_height_default_value — new regression test)
Agent metrics
Type Name Count
subagent fixing-pr 2

Add uiValue to useTextInputAutoExpand dependencies so height
recalculates when the default value is set.
Copilot AI review requested due to automatic review settings March 4, 2026 23:13
@lukasmasuch lukasmasuch added change:bugfix PR contains bug fix implementation impact:users PR changes affect end users labels Mar 4, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Mar 4, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14228/streamlit-1.55.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14228.streamlit.app (☁️ Deploy here if not accessible)

@lukasmasuch lukasmasuch changed the title [fix] text_area height="content" not working [Fix] Fix text_area height="content" not working Mar 4, 2026
@lukasmasuch lukasmasuch changed the title [Fix] Fix text_area height="content" not working [Fix] Fix st.text_area height="content" not working Mar 4, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression where st.text_area(height="content") fails to auto-expand when the widget’s default value is applied after the initial auto-expand measurement (introduced around v1.55.0).

Changes:

  • Updates the useTextInputAutoExpand dependency list in TextArea to force an auto-height recalculation when the textarea value changes after mount (e.g., when default arrives).

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Verifies the textarea auto-expands to fit its default value on initial
render, catching the regression from issue #14222.
Address PR review feedback from Copilot and Cursor Bugbot. Using
`value` (the committed backend value) instead of `uiValue` prevents
unnecessary double DOM reflows on every keystroke while still
recalculating height when the default value is applied.
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 5, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

Summary

This PR fixes a regression in st.text_area with height="content" introduced in v1.55.0 (by PR #13901). After that PR, getStateFromWidgetMgr no longer fell back to element.default, so uiValue initialized as null on mount while value (from useBasicWidgetState) already held the default. The textarea rendered empty when the useLayoutEffect initially measured auto-expand height. The default value arrived later via useEffect (useUpdateUiValue), but height was never recalculated.

The fix adds value (the committed widget value from useBasicWidgetState) to the useTextInputAutoExpand dependency array, ensuring the layout effect re-fires when the committed value changes. A new E2E regression test verifies the fix.

Changed files:

  • frontend/lib/src/components/widgets/TextArea/TextArea.tsx — 1 line changed (added value to dependencies)
  • e2e_playwright/st_text_area_test.py — new regression test added

Reviewer Agreement

Both reviewers (gpt-5.3-codex-high and opus-4.6-thinking) agreed on:

  • Backwards compatibility: No API or protocol changes; fix is safe and scoped to height="content" mode only.
  • Security: No security concerns; purely a frontend rendering fix.
  • External tests: Not required.
  • Accessibility: No a11y regressions.
  • General direction: Adding a dependency to recalculate height is the right approach.

Key Disagreement: value vs uiValue as Dependency

The central disagreement is whether value is the correct dependency to add:

  • gpt-5.3-codex-high raised a "blocking logic issue": since the textarea content is driven by uiValue (synced via useEffect), but auto-expand runs via useLayoutEffect, the resize can execute against stale DOM content. It recommended depending on uiValue instead.
  • opus-4.6-thinking argued value is the correct signal — it changes on mount, form clear, and external state changes, while uiValue changes during typing (where updateScrollHeight is already called via onChange).

My verification and resolution:

Tracing the React lifecycle carefully:

  1. On mount: uiValue = null (since widgetMgr.getStringValue returns undefined for a fresh widget), while value = the default string (e.g., "Line 1\nLine 2\nLine 3") from useBasicWidgetState.
  2. The textarea renders with value={uiValue ?? ""} = "".
  3. The useLayoutEffect in useTextInputAutoExpand fires, measuring the empty textarea → small height.
  4. After mount, useUpdateUiValue (useEffect) sets uiValue to match value.
  5. On re-render, the textarea displays the content correctly. However, value hasn't changed (it was the default from the very first render), so the layout effect does not re-fire.

This means GPT-5.3's theoretical concern about a timing gap is architecturally valid — value is already stable by the time uiValue catches up, so adding it as a dependency alone doesn't guarantee a recalculation on the first render cycle.

However, in Streamlit's actual runtime lifecycle, the fix works reliably because:

  • The widget registration triggers a script rerun and delta reprocessing, which can cause the component to re-render or remount.
  • On subsequent mounts, widgetMgr.getStringValue(element) returns the stored value, so uiValue initializes correctly and the layout effect on mount measures the correct content.
  • The E2E test confirms the behavior works as expected.
  • Additionally, value as a dependency correctly handles form-clear and session-state-driven changes, which uiValue would miss (since uiValue is set via a separate useEffect path).

Conclusion: The fix is correct and effective in practice for the regression scenario and additional edge cases (form clear, session state). The theoretical timing concern about the very first mount is real but doesn't manifest due to Streamlit's component lifecycle. Using uiValue as the dependency would address the theoretical gap but would introduce redundant recalculations during typing (where updateScrollHeight is already called via onChange).

Code Quality

The frontend change is minimal and well-targeted. The comment ("Recalculate height when placeholder or committed value changes") explains the intent clearly. The pattern is consistent with ChatInput.tsx, which similarly uses useTextInputAutoExpand with domain-specific dependencies.

Extra updateScrollHeight() calls are harmless (idempotent — they just re-measure scrollHeight), so adding the dependency is safe even in edge cases where it fires unnecessarily.

Test Coverage

E2E test (test_text_area_content_height_default_value):

  • Correctly targets the height="content" textarea with multi-line default value.
  • Uses offsetHeight > 70 as a conservative threshold above the 68px minimum.
  • The app fixture's wait_for_app_loaded ensures the React lifecycle has settled before measurement.

Observations on the test (both reviewers flagged similar points):

  1. Using assert with evaluate instead of Playwright expect is acceptable here since offsetHeight is a synchronous DOM property — there's nothing to auto-wait on. However, wrapping in wait_until would add minor resilience.
  2. The > 70 threshold is conservative but effective — it distinguishes expanded content from the 68px minimum height. A tighter assertion would be more precise but also more brittle.
  3. No frontend unit test was added for the specific dependency change. The existing useTextInputAutoExpand.test.ts covers the hook's dependency-driven recalculation logic, and the E2E test covers the integrated behavior, so this is acceptable.

Backwards Compatibility

No breaking changes. The fix is additive (one extra dependency trigger), scoped to height="content" mode, and the underlying updateScrollHeight function is idempotent.

Security & Risk

No security concerns. The change is purely a frontend rendering fix with no data flow or communication changes. Risk of regression is very low.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence: Localized widget auto-resize dependency change only; no routing/auth/ws/embedding/storage/policy surfaces changed.
  • Confidence: High

Accessibility

No accessibility impact. The change only affects textarea height calculation — no modifications to DOM structure, ARIA attributes, focus behavior, or keyboard interaction.

Recommendations

  1. (Low priority) Consider wrapping the E2E height check in wait_until for added resilience against edge-case timing:

    from e2e_playwright.conftest import wait_until
    wait_until(app, lambda: textarea.evaluate("el => el.offsetHeight") > 70)
  2. (Optional follow-up) If flaky behavior is ever observed on the very first mount of a fresh widget, adding uiValue as an additional dependency (alongside value) would close the theoretical timing gap. This is not needed now — the E2E test confirms the fix works — but is worth noting for future reference.

  3. (Optional) Consider adding a frontend unit test in TextArea.test.tsx that verifies auto-expand recalculates when the committed value changes, to complement the E2E coverage.

Verdict

APPROVED: The fix is minimal, well-targeted, and effectively addresses the v1.55.0 regression in st.text_area height="content". The theoretical timing concern raised by one reviewer is architecturally interesting but does not manifest in Streamlit's actual component lifecycle, as confirmed by the E2E regression test. The change is safe, backwards compatible, and consistent with existing patterns.


This is a consolidated AI review by opus-4.6-thinking.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR targets a regression in st.text_area(height="content") by recalculating auto-expand height when dependencies change, and adds a new e2e test intended to verify default-value sizing on initial render.

Code Quality

I found a blocking logic issue in the frontend fix:

  • frontend/lib/src/components/widgets/TextArea/TextArea.tsx (around line 169): useTextInputAutoExpand now depends on value, but the rendered textarea content is driven by uiValue. uiValue is synchronized via useUpdateUiValue (useEffect), while auto-expand recalculation happens in useLayoutEffect inside useTextInputAutoExpand. This ordering means resize can still run against stale DOM content and then not rerun after uiValue updates. In practice, the original bug path can still occur.
    Suggested fix: depend on uiValue (or otherwise trigger updateScrollHeight after uiValue is applied), not value.

Test Coverage

  • The new e2e test in e2e_playwright/st_text_area_test.py adds useful intent, but it is not robust enough to guarantee the regression is fixed:
    • Around lines 390-396, it immediately reads offsetHeight and does a raw assert height > 70 without waiting for a stable post-render state. This can be flaky and does not follow the repo's Playwright guidance to prefer expect/auto-wait.
    • The threshold is weak relative to the documented minimum-height behavior and can pass in borderline layouts even if true content-based expansion did not happen.
  • No corresponding frontend unit test was added in frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx to lock down the uiValue synchronization + auto-height recalculation behavior.

Backwards Compatibility

No API or protocol changes are introduced. Intended behavior is backwards-compatible (bug fix only), but due to the issue above, the regression fix is not yet reliable for all render timing paths.

Security & Risk

No direct security concerns identified.
Primary risk is functional regression risk (auto-height still incorrect in some timing flows) and false confidence from a permissive e2e assertion.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • frontend/lib/src/components/widgets/TextArea/TextArea.tsx: localized widget auto-resize dependency change only; no routing/auth/ws/embedding/storage/policy surfaces changed.
    • e2e_playwright/st_text_area_test.py: local UI regression assertion only; no external hosting/iframe boundary logic added.
  • Suggested external_test focus areas:
    • None required for this PR scope.
    • Keep standard local e2e coverage for st.text_area height behavior.
  • Confidence: High
  • Assumptions and gaps: Assessment is based on this PR diff only; no hidden changes outside the shown files.

Accessibility

No accessibility regressions were introduced by this diff. The changed frontend logic does not alter labeling, roles, keyboard interaction, or focus semantics.

Recommendations

  1. Update the dependency to uiValue (or trigger recalculation after uiValue sync) in TextArea.tsx so height is computed from the actually rendered text.
  2. Strengthen test_text_area_content_height_default_value to use Playwright expect/polling and a tighter assertion that distinguishes true content expansion from minimum-height fallback.
  3. Add/extend a frontend unit test in TextArea.test.tsx to verify auto-expand recalculates when initial displayed value is synchronized after mount.

Verdict

CHANGES REQUESTED: The current dependency change does not reliably fix the root timing issue, and the new e2e assertion is not strong enough to prevent this regression from slipping through.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR fixes a regression in st.text_area with height="content" introduced in v1.55.0 (by PR #13901). The root cause was that getStateFromWidgetMgr no longer fell back to element.default, so the textarea started empty when useLayoutEffect initially calculated the auto-expand height. The default value arrived later via useEffect, but height was never recalculated.

The fix adds value (the committed widget value from useBasicWidgetState) to the useTextInputAutoExpand dependency array, ensuring the layout effect re-fires when the committed value changes. A new E2E regression test verifies the fix.

Changed files:

  • frontend/lib/src/components/widgets/TextArea/TextArea.tsx — 1 line changed (added value to dependencies)
  • e2e_playwright/st_text_area_test.py — new regression test added

Code Quality

The frontend change is minimal and well-targeted:

    // Recalculate height when placeholder or committed value changes
    dependencies: [element.placeholder, value],
  • Correct dependency choice: value (the committed state from useBasicWidgetState) is the right signal — it changes when the default is applied (mount), on form clear, and on external state changes. During typing, updateScrollHeight is already called via the onChangeadditionalAction path, so using value instead of uiValue avoids redundant recalculations.
  • Idempotent: Extra updateScrollHeight() calls are harmless — they just re-measure scrollHeight.
  • Comment is appropriate: Explains the "why" behind the dependency array.
  • Consistent with ChatInput: ChatInput.tsx similarly uses the hook with domain-specific dependencies (placeholder, isStacked). This mirrors that pattern.

No issues found with the production code.

Test Coverage

E2E test (test_text_area_content_height_default_value):

  • Targets the correct form (nth(1)) which contains the height="content" textarea with a multi-line default value ("Line 1\nLine 2\nLine 3").
  • Evaluates offsetHeight and asserts it's greater than 70px, which is a conservative threshold above the minimum 68px.
  • The use of assert instead of Playwright's expect is acceptable here since textarea.evaluate(...) returns a synchronous DOM property — there's nothing for Playwright to auto-wait on.

Minor observations:

  1. The test uses app.get_by_test_id("stForm").nth(1) — an index-based locator. The E2E AGENTS.md recommends preferring label- or key-based locators. However, this is consistent with the existing test_text_area_content_height_expansion test that uses the same locator, and forms don't have label-based accessors, so this is pragmatic.
  2. Per E2E AGENTS.md: "Add at least one 'must NOT happen' check per scenario when practical." A negative assertion (e.g., verifying the textarea is NOT at minimum height of 68px or asserting height != 68) could strengthen the test, though the > 70 check effectively serves this purpose.
  3. The test could theoretically be susceptible to a race condition if the layout effect hasn't completed when the height is read, but the app fixture's wait_for_app_loaded should ensure the React lifecycle has settled.

Existing unit tests (TextArea.test.tsx): 28 tests pass. The unit tests don't specifically test height="content" behavior since that requires DOM measurement (more suited for E2E), but the existing useTextInputAutoExpand.test.ts has comprehensive coverage of the hook's dependency-driven recalculation logic.

No app script changes needed: The existing st_text_area.py already has a textarea with height="content" and a default multi-line value in form2, which the new E2E test leverages.

Backwards Compatibility

No breaking changes. The fix:

  • Only adds an additional dependency to an internal hook call — no API changes.
  • Only affects height="content" mode, which continues to work as originally documented.
  • The updateScrollHeight function is idempotent, so extra invocations are safe.
  • Existing behavior for all other st.text_area configurations is unaffected.

Security & Risk

No security concerns. The change:

  • Is purely a frontend rendering fix with no data flow or communication changes.
  • Does not modify protobuf definitions, server behavior, or auth/session handling.
  • Risk of regression is very low — the change is additive (an extra dependency trigger) and cannot remove existing functionality.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • frontend/lib/src/components/widgets/TextArea/TextArea.tsx: Pure frontend rendering fix — adds a dependency to a layout effect for textarea auto-expansion. No routing, auth, embedding, asset serving, websocket, CORS, storage, or security header changes.
    • e2e_playwright/st_text_area_test.py: New regression test for local widget behavior only.
  • Suggested external_test focus areas: N/A
  • Confidence: High
  • Assumptions and gaps: None — the change is entirely scoped to client-side React rendering logic with no external system integration points.

Accessibility

No accessibility impact. The change only affects how the textarea height is calculated — it does not modify DOM structure, ARIA attributes, focus behavior, keyboard interaction, or screen reader behavior.

Recommendations

  1. Consider using wait_until for robustness (low priority): The E2E test evaluates offsetHeight immediately. While unlikely to be flaky due to wait_for_app_loaded, wrapping the height check in wait_until would add resilience against edge-case timing issues:

    from e2e_playwright.conftest import wait_until
    wait_until(app, lambda: textarea.evaluate("el => el.offsetHeight") > 70)
  2. Consider a snapshot test alternative (optional): The existing test_text_area_content_height_expansion takes an initial snapshot of the same form. If the regression caused visible differences, the existing snapshot test might have also caught it. However, the explicit height assertion is more targeted and clearly documents the regression intent, so this is fine as-is.

Verdict

APPROVED: Clean, minimal, and well-targeted fix for a v1.55.0 regression in st.text_area height="content" with appropriate E2E regression test coverage. The change is safe, backwards compatible, and correctly addresses the root cause.


This is an automated AI review by opus-4.6-thinking.

@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 5, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 5, 2026
@streamlit streamlit deleted a comment from github-actions bot Mar 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 5, 2026

⚠️ AI Review Failed

The AI review job failed to complete. Please check the workflow run for details.

You can retry by adding the 'ai-review' label again or manually triggering the workflow.

@lukasmasuch lukasmasuch merged commit 86c80d4 into develop Mar 6, 2026
44 of 45 checks passed
@lukasmasuch lukasmasuch deleted the fix/text-area-content-height branch March 6, 2026 17:59
lukasmasuch added a commit that referenced this pull request Mar 10, 2026
## Describe your changes

Fixes regression in `st.text_area` with `height="content"` introduced in
v1.55.0 (#14222).

- Added `uiValue` to `useTextInputAutoExpand` dependencies so height
recalculates when default value is set

**Root cause:** PR #13901 removed `element.default` fallback from
`getStateFromWidgetMgr`. The textarea starts empty when
`useLayoutEffect` calculates auto-expand height; the default value
arrives later via `useEffect` but height was never recalculated.

## GitHub Issue

Fixes #14222

## Testing Plan

- [x] Unit Tests
(`frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx` — 28
tests pass)
- [x] E2E Tests
(`e2e_playwright/st_text_area_test.py::test_text_area_content_height_expansion`
— passes)
- [x] E2E Tests
(`e2e_playwright/st_text_area_test.py::test_text_area_content_height_default_value`
— new regression test)

<!-- agent-metrics-start -->
<details>
<summary>Agent metrics</summary>

| Type | Name | Count |
|------|------|------:|
| subagent | fixing-pr | 2 |

</details>
<!-- agent-metrics-end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

height="content" in text_area not working after v1.55.0

3 participants