Customizing the character counter on Text and Textarea options
By default, WooCommerce Product Options counts the visible characters that a shopper enters into a Text or Textarea option — whitespace (spaces, tabs, newlines) is excluded from the total. This default keeps the on-page counter consistent with what shoppers visually perceive as "characters", and avoids surprises on Text/Textarea options that price by character count, where adding a few extra spaces could otherwise inflate the fee.
By default, WooCommerce Product Options counts the visible characters that a shopper enters into a Text or Textarea option — whitespace (spaces, tabs, newlines) is excluded from the total. This default keeps the on-page counter consistent with what shoppers visually perceive as "characters", and avoids surprises on Text/Textarea options that price by character count, where adding a few extra spaces could otherwise inflate the fee.
In some scenarios, though, the inverse behaviour is exactly what the merchant needs:
- An invitation message where every character — spaces included — affects the print layout and should be charged for accordingly.
- A monogramming service that only wants letters counted, ignoring numbers and punctuation a shopper might type by mistake.
- An option where the counter should disregard everything except a very specific set of characters defined by the merchant.
Two filters control how characters are counted. They serve different purposes and, in stores that charge based on character count, they should normally be configured to behave the same way.
The two filters
1. The on-page character counter
The counter shown under the Text/Textarea field as the shopper types reads a small pattern from a JavaScript setting called charCountRegExp. The setting holds a regular expression character class fragment describing which characters the counter should accept — anything outside that set is dropped before counting.
The default value is \S, which matches every non-whitespace character. Anything else (spaces, tabs, newlines) is stripped from the value before the length is measured.
To override the default, hook the wc_product_options_script_params filter and replace the value:
<?php
add_filter( 'wc_product_options_script_params', 'my_custom_char_count_regex' );
function my_custom_char_count_regex( $params ) {
$params['charCountRegExp'] = '\s\S';
return $params;
}
The example above tells the counter to keep every character (both whitespace and non-whitespace), so spaces, tabs and newlines all count toward the total.
The pattern is inserted inside a JavaScript character class — [^…] — so use the same syntax you would use inside square brackets in any regular expression. A few useful examples:
\S— non-whitespace only (this is the default).\s\S— every character, including spaces and newlines.a-zA-Z— Latin letters only; numbers, punctuation, and whitespace are ignored.a-zA-Z0-9— letters and digits, but not punctuation or whitespace.
\p{L} for "any letter in any script" are not supported in this setting. For Unicode-aware counting, please contact us with the specific scripts you need to support.2. The character count billed at the cart
If a Text or Textarea option is priced by character count, the figure that determines how much the shopper is charged is computed on the server when the item is added to the cart — not in the browser. By default the server applies the same rule as the on-page counter and strips whitespace before counting, so the visible counter and the billed count match.
A separate filter, wc_product_options_cart_char_count, controls the billed count. Use it whenever you change charCountRegExp on a char-count-priced option, otherwise the on-page counter and the cart total will disagree — and shoppers tend to trust the cart figure.
The filter receives the already-computed default (non-whitespace count), the raw submitted value, and the field instance, so you can either return a completely custom count or branch on which option you're filtering.
Example — include every character (matching the on-page filter above):
<?php
add_filter( 'wc_product_options_cart_char_count', 'my_custom_cart_char_count', 10, 3 );
function my_custom_cart_char_count( $count, $value, $field ) {
return mb_strlen( $value );
}
Example — count only Latin letters (matching charCountRegExp = 'a-zA-Z'), and only for a specific option (so other char-count options keep the default behaviour):
<?php
add_filter( 'wc_product_options_cart_char_count', 'my_custom_cart_char_count_letters_only', 10, 3 );
function my_custom_cart_char_count_letters_only( $count, $value, $field ) {
if ( $field->get_id() !== '1234' ) {
return $count;
}
return mb_strlen( preg_replace( '/[^a-zA-Z]/u', '', (string) $value ) );
}
Why two filters?
The two filters target two distinct surfaces:
- The on-page counter is part of the shopper's display. It provides immediate feedback as they type and updates the running price preview on the product page.
- The cart-stored count is the billing figure. It's resolved server-side, after the form is submitted, and is what WooCommerce uses to calculate the fee.
If you only change one of the two on a char-count-priced option, the on-page price preview will use one rule and the cart line will use another. The default behaviour makes both filters agree out of the box — only override one of them on its own if you deliberately want the preview and the cart to differ (for instance, to show a friendlier counter that always counts every character, while still billing for non-whitespace only).
For a Text or Textarea option that is not priced by character count (the price type is anything other than Per character), only the on-page counter matters — the wc_product_options_cart_char_count filter is never invoked for those options.
Related Articles
- Searching for groups
- Code snippet: How to bundle add-on products with the main product
- Display font names in their actual typeface in a dropdown
- Code snippet - Itemize the tax in the product options price display
- Code snippet: Display a product option based on the quantity selected
- How to add custom code snippets to your website