Skip to content

[Bug]: input_text_area applies the width:100% rule to the textarea in the inverted case #2507

Open

Description

@Rodrigo-Palma

Component

UI (ui.*)

Severity

P2 - Medium (workaround exists)

Shiny Version

1.8.0 (also present on main at 232d2d9)

Python Version

3.12.13

Minimal Reproducible Example

from shiny import ui

print(ui.input_text_area("x", "L", cols=20))
print(ui.input_text_area("x", "L", width="600px", cols=20))

Rendered output:

<!-- cols=20, no width: the textarea gets width:100%, so cols is dead -->
<div class="shiny-input-textarea form-group shiny-input-container">
  <label class="control-label" id="x-label" for="x">L</label>
  <textarea id="x" class="form-control" style="width:100%;" cols="20" data-update-on="change"></textarea>
</div>

<!-- width=600px: the container is 600px, the textarea is ~20 characters wide -->
<div class="shiny-input-textarea form-group shiny-input-container" style="width:600px;">
  <label class="control-label" id="x-label" for="x">L</label>
  <textarea id="x" class="form-control" cols="20" data-update-on="change"></textarea>
</div>

Behavior

Current: the width:100% rule is applied to the <textarea> exactly when width is not given, and omitted when it is given.

Expected: the opposite, which is what the docstring of cols describes (shiny/ui/_input_text.py:122-126):

This argument will only take effect if there is not a CSS width rule defined for this element; such a rule could come from the width argument of this function [...]

So the documented contract is: pass width and the CSS rule wins, omit it and cols decides. What happens is the reverse, at shiny/ui/_input_text.py:186:

style=css(width=None if width else "100%", height=height, resize=resize),

Two user visible consequences:

  1. cols= never takes effect in the plain case, the one the docs say it applies to.
  2. width="600px" widens the wrapper <div> but leaves the field itself at its cols size, so the input does not stretch to the width that was asked for. This is the one users notice.

Shiny for R, which this function is a port of (the docstring is a direct port too), has the condition the other way around (R/input-textarea.R):

style = css(
  width = if (!is.null(width)) "100%",
  height = validateCssUnit(height), resize = resize
),

There the outer div carries the user width and the textarea fills it with 100%, which is the behaviour the py-shiny docstring describes. The py-shiny div already carries the user width the same way, so only the inner condition is out of step.

Error Messages (if any)

(no error, it is a layout difference)

Environment

  • OS: macOS 15
  • Browser: not applicable, the difference is visible in the rendered HTML
  • Dependencies: shiny 1.8.0, Python 3.12.13

Additional context

This is a visual change to a widely used input, which is why I am opening an issue instead of a PR. Apps that today rely on the accidental width:100% when no width is passed would see the textarea shrink to its cols size, and the default cols for input_text_area means most of them would notice.

Possible paths:

  • flip the condition to match the docstring and Shiny for R (one line), and call it out in the changelog as a visual change;
  • or keep the current rendering and rewrite the cols docstring, accepting that cols is effectively unusable unless width is set.

I have the one line fix plus a rendering test ready and I am happy to open a PR for whichever direction you prefer.

Activity

  1. cpsievert commented on Sep 28, 2026

    @cpsievert
    Collaborator

    Thanks for the detailed write-up, we'd be happy to review a PR.

  2. added
    Priority: LowLow-impact bug, docs polish, papercut, or unclear low-severity request.
    ai-triage:doneMarks an issue whose AI triage workflow is complete.
    on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: LowLow-impact bug, docs polish, papercut, or unclear low-severity request.ai-triage:doneMarks an issue whose AI triage workflow is complete.needs-triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions