# Builder max min character Limit for localized fields

**URL:** <https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918>\
**Category:** General\
**Tags:** react, nextjs, locale\
**Created:** [November 5, 2025, 1:27pm UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918 "2025-11-05T13:27:08Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![dipanjanbhattacharya](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dipanjanbhattacharya](https://forum.builder.io/u/dipanjanbhattacharya)\
**Post date:** [November 5, 2025, 1:27pm UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918/1 "2025-11-05T13:27:08Z")

</div>

In [Builder.io](http://Builder.io) in our page and section model for our custom component we want to add a max and min character limit, that we have achieved but we have several locales, how to acheive that for the various locales in our builder registry .  
We are not able to get the particular locale for which we need to check.

please see the title field in the following custom component. I am searching for locale in options and context but dont get it

{

```
      name: 'title',

      type: 'string',

      friendlyName: 'Title',

      helperText: 'Overlay Card: Main heading text',

      onChange: (options) => {

      

        console.log('locale',options.get('locale'))

          console.log('options',options);

        const typeTitle = options.get('title');

        const titleObj =

          typeof typeTitle === 'string'

            ? typeTitle

            : typeTitle && typeof typeTitle.get === 'function'

              ? typeTitle.get('en-CA') || typeTitle.get('Default') || ''

              : '';

        if (titleObj.length > 40) {

          alert('Title size should not Exceed 40 characters');

          typeof typeTitle === 'string'?

           options.set('title', options.get('title').substring(0, 40)):

           '';

        }

      },

    },

```

---

<div class="post-metadata">

**Author:** ![manish-sharma](https://avatars.discourse-cdn.com/v4/letter/m/8797f3/32.png) [@manish-sharma](https://forum.builder.io/u/manish-sharma)\
**Post date:** [November 7, 2025, 1:58pm UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918/2 "2025-11-07T13:58:16Z")

</div>

Hello @dipanjanbhattacharya ,

The issue was that the locale wasn’t directly accessible through the standard `options` or `context` in the Builder registry. However, the following implementation successfully retrieves the current locale from the Builder state context and applies both minimum and maximum character limits per locale.

Here’s the tested code snippet that works well:

```auto
Builder.registerComponent(Heading, {
  name: "Heading",
  inputs: [
    {
      name: "text",
      type: "string",
      defaultValue: "Add a headline",
      localized: true,
      helperText: "Supports per-locale copy with a 10-120 character range.",
      onChange: (options) => {
        const currentLocale =
          (options.builder &&
            options.builder.state &&
            options.builder.state.context &&
            options.builder.state.context.locale) ||
          options.locale;
        const value = options.get("text") as
          | string
          | Record<string, unknown>
          | undefined
          | null;
        const minLength = 4;
        const maxLength = 12;
        const globalScope =
          typeof globalThis === "object" && globalThis !== null
            ? (globalThis as typeof globalThis & {
                alert?: (value: string) => void;
              })
            : undefined;
        const showAlert = (message: string) => {
          if (typeof globalScope?.alert === "function") {
            globalScope.alert(message);
            return;
          }

          if (typeof alert === "function") {
            alert(message);
          }
        };

        if (typeof value === "string") {
          if (value.length > maxLength) {
            options.set("text", value.slice(0, maxLength));
            showAlert(`Maximum length of ${maxLength} reached${currentLocale ? ` for locale ${currentLocale}` : ""}.`);
          }

          if (value.length !== 0 && value.length < minLength) {
            showAlert(
              `${currentLocale ? `Locale ${currentLocale}` : "Heading text"} must have at least ${minLength} characters.`,
            );
          }
          return;
        }

        if (!value || typeof value !== "object") {
          return;
        }

        const mapCandidate = value as unknown as Map<string, unknown>;
        const isMapLike =
          typeof mapCandidate?.forEach === "function" &&
          typeof mapCandidate?.entries === "function";

        const entries = isMapLike
          ? Array.from(mapCandidate.entries())
          : Object.entries(value as Record<string, unknown>);

        let didModify = false;

        entries.forEach(([locale, localeValue]) => {
          if (locale.startsWith("@") || typeof localeValue !== "string") {
            return;
          }

          if (localeValue.length > maxLength) {
            const truncated = localeValue.slice(0, maxLength);
            if (isMapLike) {
              mapCandidate.set(locale, truncated);
            } else {
              (value as Record<string, unknown>)[locale] = truncated;
            }
            didModify = true;
            showAlert(`Maximum length of ${maxLength} reached for locale ${locale}.`);
          }

          if (localeValue.length !== 0 && localeValue.length < minLength) {
            showAlert(`Locale ${locale} must have at least ${minLength} characters.`);
          }
        });

        if (!isMapLike && didModify) {
          options.set("text", { ...(value as Record<string, unknown>) });
        }
      },
    },
    {
      name: "level",
      type: "string",
      enum: ["h1", "h2", "h3", "h4", "h5", "h6"],
      defaultValue: "h2",
    },
    {
      name: "align",
      type: "string",
      enum: ["left", "center", "right"],
      defaultValue: "left",
    },
  ],
});

```

> **[Input types](https://www.builder.io/c/docs/input-types#onChange)**
>
> Learn about the input types for custom components and data models. This document includes required inputs, optional inputs, and examples

> [@How to get the value of a text box inside a container to update the container style based on the value of the text box?](https://forum.builder.io/t/how-to-get-the-value-of-a-text-box-inside-a-container-to-update-the-container-style-based-on-the-value-of-the-text-box/4839):
>
> Builder content link Builder public api key ce289f18ea2a4567bfcc5038d6ea2892 I have a container “Footer” that i’m trying to change the background color of based on the value of the text box that is inside this container. I’m having some difficulty understanding how to do this in builder using the element data binding. I’ve added a screenshot of the visual editor to show the layer container “Footer” that contains the text box which will either show a text of Register, Filled, Ended, or Cancel…

Thanks,

---

<div class="post-metadata">

**Author:** ![dipanjanbhattacharya](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dipanjanbhattacharya](https://forum.builder.io/u/dipanjanbhattacharya)\
**Post date:** [November 13, 2025, 8:46pm UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918/3 "2025-11-13T20:46:42Z")

</div>

Alert seems a bad UX. Can we set a red border with a custom message especially for a rich text field

@manish-sharma

---

<div class="post-metadata">

**Author:** ![ayushkumarshaw\_ama](https://avatars.discourse-cdn.com/v4/letter/a/ac91a4/32.png) [@ayushkumarshaw\_ama](https://forum.builder.io/u/ayushkumarshaw_ama)\
**Post date:** [January 9, 2026, 5:54am UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918/4 "2026-01-09T05:54:27Z")

</div>

Hi @manish-sharma we used the code and made it a function, and it is working fine but I am facing a challenge with a component

```auto
Builder.registerComponent(EditorialGridBuilder, {
  name: 'EditorialGrid',
  inputs: [
    {
      name: 'editorialType',
      type: 'string',
      defaultValue: 'Story',
      enum: ['Story', 'EditorialCardUsp'],
      required: true,
    },
    {
      name: 'EditorialCardProps',
      type: 'object',
      subFields: [
        {
          name: 'title',
          type: 'string',
          defaultValue: 'Stories from Ama',
          required: false,
        },
        {
          name: 'description',
          type: 'richText',
          required: false,
        },
        {
          name: 'buttonText',
          type: 'string',
          defaultValue: 'See all stories',
          required: false,
          helperText: 'Text for the button. Leave empty to hide the button.',
        },
        // Button URL fields - using helpers
        ...urlFieldGroup({
          urlTypeFieldName: 'buttonUrlType',
          manualFieldName: 'buttonManualUrl',
          internalFieldName: 'buttonInternalUrl',
        }),
        {
          name: 'cards',
          type: 'list',
          subFields: [
            imageField({
              fieldName: 'Image',
              required: true,
              allowedTypes: ['.jpg', '.jpeg', '.png', '.webp'],
              includeAltText: true,
              altTextDefault: 'Editorial image',
            }),
            {
              name: 'title',
              type: 'string',
              required: false,
              helperText: 'length should be between 40-90 characters',
              onChange: characterValidationHandler({
                //minLength: 40,
                maxLength: 90,
                fieldName: 'title',
                fieldType: 'string',
              }),

              // onChange: (options) => {
              // const editorialType = options.get('editorialType');
              // const titleValue = options.get('title');
              // if (editorialType === 'Story') {
              // return characterValidationHandler({
              // //minLength: 40,
              // maxLength: 90,
              // fieldName: 'title',
              // fieldType: 'string',
              // })(titleValue);
              // }
              // if (editorialType === 'EditorialCardUsp') {
              // return characterValidationHandler({
              // //minLength: 5,
              // maxLength: 22,
              // fieldName: 'title',
              // fieldType: 'string',
              // })(titleValue);
              // }
              // return true;
              // },
            },
            {
              name: 'description',
              type: 'richText',
              required: true,
              helperText: 'length should be between 400-500 characters',
              onChange: characterValidationHandler({
                //minLength: 400,
                maxLength: 500,
                fieldName: 'description',
                fieldType: 'richText',
              }),
            },
            {
              name: 'tags',
              type: 'string',
              defaultValue: '',
              required: false,
              helperText: 'Comma-separated list of tags (e.g., "Insider guides, Life Onboard")',
            },
            ...urlFieldGroup({
              manualFieldName: 'manualUrl',
              internalFieldName: 'cardurl',
              manualHelperText:
                'Enter a full URL (e.g., https://...) or internal path (/stories/article-1).',
            }),
          ],
        },
      ],
      defaultValue: EditorialCardData,
    },
  ],
});

```

As you can see we are using a field “name: ‘editorialType’,” , and based on this showing different UI variant of a component.  
In this component title field we want the limits to be different based on “editorialType” but unable to do so cause no if else is working even the editorialType is not accessible within this block we can’t get its value the function “characterValidationHandler” is the function implementation for your provided code which takes the max length value. can you help us with that please.

---

<div class="post-metadata">

**Author:** ![sancharighosh1](https://avatars.discourse-cdn.com/v4/letter/s/48db29/32.png) [@sancharighosh1](https://forum.builder.io/u/sancharighosh1)\
**Post date:** [January 29, 2026, 7:20am UTC](https://forum.builder.io/t/builder-max-min-character-limit-for-localized-fields/15918/5 "2026-01-29T07:20:02Z")

</div>

Hi @manish-sharma ,

Can you please help on this issue?
