---
title: Render a character-counting textarea
summary: Emit a labeled textarea with client-side character-count data attributes.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-09-17
library: SAIF.Framework.Web.Mvc
reference_type: package
operation_id: OP-22
trigger: Razor view calling the helper
---

# Render a character-counting textarea

Emit a labeled textarea with client-side character-count data attributes. Trigger: Razor view calling the helper.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-22 |
| **Trigger** | Razor view calling the helper |
| **Inputs** | Field name or lambda, optional label text, html attributes, `startLength` (default 0), `maxLength` (default 4000) |
| **Outputs** | `MvcHtmlString` — label or span markup followed by the `<textarea>` |
| **Primary components** | `TextCounterHelper.TextAreaCounter` and `TextAreaCounterFor` |
| **Entry point** | `src/Web.Mvc/UI/Extensions/TextCounterHelper.cs` |

## Steps

1. `TextAreaCounterFor` resolves the property name from the expression and delegates to `TextAreaCounter` without forwarding `maxLength`, so the strongly-typed overload always applies the 4000 default ([BR-52](../business-rules.md#br-52-strongly-typed-textarea-counter-always-uses-the-4000-default)).
2. Compute the element `id` by replacing `.` with `_` ([BR-47](../business-rules.md#br-47-generated-element-ids-sanitize-dots-to-underscores)); emit `id`, `name`, `data-start-length`, and `data-max-length`, then merge caller attributes.
3. Add CSS class `js-bo-text-counter` — the client-side counter hook.*
4. When `labelText` is blank emit a bare `<span>`; otherwise emit `<label class="control-label" for="{id}">{labelText}</label>` ([BR-53](../business-rules.md#br-53-textarea-counter-without-label-text-renders-a-bare-span)).
5. Return the label markup concatenated with the textarea markup.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([Razor view calls TextAreaCounter])
	ForOverload{Called via TextAreaCounterFor?}
	Default[Apply default maxLength 4000]
	Id[Compute sanitized element id]
	Label{labelText blank?}
	Span[Emit bare span]
	LabelEl[Emit label element]
	Textarea[Emit textarea]
	EndOk([Return combined markup])
	Start --> ForOverload
	ForOverload -->|Yes| Default
	ForOverload -->|No| Id
	Default --> Id
	Id --> Label
	Label -->|Yes| Span
	Label -->|No| LabelEl
	Span --> Textarea
	LabelEl --> Textarea
	Textarea --> EndOk
```

## Failure and edge-case behavior

- No unexpected failure paths beyond argument-guard exceptions and delegated dependency behavior were identified for this operation.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.


## Sources
- `src/Web.Mvc/UI/Extensions/TextCounterHelper.cs`
