Soft Limit Function on a Craft CMS Text Field Using a Module

A short tutorial showing how to add a soft limit on characters for a Craft CMS text field. We use a module to achieve this functionally without using a plugin. A module is a great way to include website/client-centric logic in Craft CMS.

Screenshot 2022 10 25 171529

Set up module structure

Inside your Craft CMS project's root create the following directory structure for the new module, along with the main module class SiteModule.php.

├─── Module.php
└─── sitemodule/
    └─── src/
        ├─── SiteModule.php
        └─── assets/
            ├─── SiteAssets.php
            └─── js/
                └─── site.js

Set up class autoloading

Tell Composer how to find your module’s classes by setting the autoload field in your project’s composer.json file.

"autoload": {
    "psr-4": {
      "modules\\sitemodule\\": "modules/sitemodule/src/"

With that in place, go to your project’s directory in your terminal, and run the following command:

composer dump-autoload -a

That will tell Composer to update its class autoloader script based on your new autoload mapping.

Update the application config

We can then add the module to your project’s application configuration by listing it in the modules and bootstrap arrays. This can be done by editing the project's config/app.php file:


use craft\helpers\App;

return [
    'id' => App::env('APP_ID') ?: 'CraftCMS',
    'modules' => [
        'site-module' => \modules\sitemodule\SiteModule::class,
    'bootstrap' => ['site-module'],

The main module class

The SiteModule.php file is your module’s entry point for the system. Its init() method is the best place to register event listeners, and any other steps it needs to take to initialize itself. Add the following code to your SiteModule.php file.

namespace modules\sitemodule;

use modules\sitemodule\assets\SiteAssets;

use Craft;
use craft\events\TemplateEvent;
use craft\web\View;

use yii\base\Event;
use yii\base\Module;
class SiteModule extends Module
    public function init(): void


        Craft::setAlias('@sitemodule', __DIR__);

        if (Craft::$app->getRequest()->getIsCpRequest()) {
                function (TemplateEvent $event) {


Create an Asset Bundle

We then create an Asset Bundle so we can register specific CSS and JS files Add the following asset bundle to your SiteAssets.php file.

namespace modules\sitemodule\assets;

use craft\web\AssetBundle;
use craft\web\assets\cp\CpAsset;

class SiteAssets extends AssetBundle

    public function init(): void
        $this->sourcePath = '@sitemodule/assets';

        $this->depends = [

        $this->js = [


Soft Limit JavaScript

So inside of the site.js is where we add our JavaScript for our soft limit functionality. We specifically target field-summary in my example. summary being the handle of the text field we are limiting. Original CodePen

let textArea = document.getElementById("fields-summary");
let characterCounter = document.createElement('span');
characterCounter.textContent = '250/250';

const maxNumOfChars = 250;

function insertAfter(referenceNode, newNode) {
  referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);

if (textArea) {
  insertAfter(textArea, characterCounter);

  const countCharacters = () => {
    let numOfEnteredChars = textArea.value.length;
    let counter = maxNumOfChars - numOfEnteredChars;
    characterCounter.textContent = counter + "/250";
    if (counter < 0) { = "red";
    } else if (counter < 20) { = "orange";
    } else { = "black";
  window.addEventListener('load', countCharacters, false);
  textArea.addEventListener("input", countCharacters, false);

I'm a no-nonsense, experienced website developer who works with Content Management Sytems and specializes in Craft CMS.

Lets Talk Today!