Repository navigation
[Bug]: input_text_area applies the width:100% rule to the textarea in the inverted case #2507
Description
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:
cols=never takes effect in the plain case, the one the docs say it applies to.width="600px"widens the wrapper<div>but leaves the field itself at itscolssize, 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
colsdocstring, accepting thatcolsis effectively unusable unlesswidthis 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
cpsievert commentedon Sep 28, 2026
Thanks for the detailed write-up, we'd be happy to review a PR.