Custom ProcessWire Module Development: Hooks, Fieldtype, Process

Standard CMS capabilities often fall short of your processes, and the team starts manually moving data. We develop custom ProcessWire modules turnkey: we automate routine tasks, eliminate manual input errors, and reduce operation time. Let's discuss your task — we'll come back with an estimate and a plan.

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Frequently Asked Questions

When standard CMS logic is not enough: a custom ProcessWire module

Built-in fields and templates cover a typical content site: pages, categories, relations, conditional output.

The limit is reached when logic starts to depend on data rather than on structure: calculating cost from a set of parameters, syncing a catalog with an external system, per-record permissions, scenarios where one operation changes the state of several related entities.

At that point, the built-in fields are no longer enough, and workarounds start appearing in templates — queries inside loops, logic in markup, duplicated checks.

Such code works until the first change in requirements, and after that every modification drags along a re-check of everything nearby.

Extending the CMS through a custom ProcessWire module moves this logic to where it is visible and verifiable: into a separate class with hook points into the core.

Hooks let you plug into the right moment of the lifecycle — before a page is saved, after a field changes, during a query — without touching the rest of the code.

A custom field type (Fieldtype) describes data storage and validation at the core level, so the value behaves like a built-in one: it is visible in the admin, queries work against it, and it survives updates.

A Process adds its own screen to the control panel — for example, to trigger a sync or process a queue — instead of keeping these operations in separate scripts.

The cost of this approach is lower than it seems at the start.

The logic lives in one place, it is covered by tests rather than checked by hand in production; when the core is updated, the module stays compatible because it relies on documented extension points rather than on the internals of templates.

Non-standard logic stops being a fragile add-on and becomes part of a system with predictable behavior.

Below we break down how this is handled in practice: which extension points we choose for a task, how we separate storage from behavior, and what remains with the client after delivery.

What a custom ProcessWire module provides: hooks, Fieldtype, Process

A custom ProcessWire module is assembled from three independent extension mechanisms: event interception, custom field types, and administrative processes. They are combined to fit the task — each one addresses its own class of limitations and delivers a measurable effect.

Event interception: logic without modifying the core

Hooks (addHookBefore, addHookAfter, addHookProperty) are attached to core methods and are called before or after them.

This makes it possible to change the behavior of page saving, queries, and rendering without touching the source code and without losing updatability: after a branch upgrade, the modifications remain in the module rather than in a fork.

In practice, this eliminates an entire class of regressions — the typical scenario of "a core modification that was forgotten to be carried over" simply stops occurring.

A custom field type: structured data instead of a dumping ground

A Fieldtype paired with an Inputfield describes a new field type: how the value is stored in the table, how it is validated, how it is rendered in the editor.

Instead of a text field with serialized JSON, the client gets a typed structure — a composite address, a reference directory, a set of related records.

Queries on such a field go through indexes rather than parsing a string on every request, so a catalog with tens of thousands of pages responds predictably.

Process module: a dedicated admin section

A Process module registers a separate page in the administrative panel with its own request handler and access rights.

This moves routine operations — import, bulk editing, reconciliation with an external system — into an interface for the content manager, instead of a script run by a developer. The editor no longer has to wait for an engineer for every batch of edits.

Option When it fits Limitations
Hooks We change the behavior of existing logic Do not create new entities and fields
Fieldtype + Inputfield A new data structure with validation is needed Requires a well-thought-out storage schema
Process module A dedicated admin section and rights Does not affect frontend output
External service Logic lives outside the CMS Duplicated access and synchronization

The mechanics are convenient to verify on a live example — the ProcessWire documentation describes each of the three interfaces separately, including the order in which hooks are called and the requirements for Fieldtype methods.

What this yields in the end: core updates are installed without conflicts, the data structure remains typed, and routine operations move out of the developer's queue into the editor's interface.

How we design and implement a module: from audit to handover

Work on a custom ProcessWire module starts from the current state of the project, not from an abstract template: first we analyze how the logic is structured today, then we fix the module's design, and only after that do we write the code.

This order eliminates the main risk — a module that formally works but conflicts with existing hooks and fields.

  1. Audit of the current logic. We look at which hooks already intercept events, where business logic is duplicated in templates, which fields and page types are involved. The output is a map of extension points and a list of places where it is safe to integrate.
  2. Module design. We define the composition: which hooks we connect, whether a custom Fieldtype is needed for data storage, whether a Process page in the admin is required for management. We fix the module configuration, access rights, and failure behavior.
  3. Data schema and migrations. We describe which fields and tables appear, how they relate to existing pages, and what happens when the module is updated. We separately think through reversibility: how to roll back if the new version has to be removed.
  4. Implementation. We write the module with separation into layers: hooks are responsible for intercepting events, Fieldtype — for storage and validation, Process — for the admin interface. The logic does not sprawl across templates; it can be changed in one place.
  5. Testing on a copy. We deploy the module on a copy of the production site with real data: we run through creation, editing, and deletion scenarios, check behavior under load and in edge cases. We catch errors before deployment, not after.
  6. Deployment and monitoring. We install the module on the production site with the ability to quickly disable it, and we monitor logs and response. If something goes wrong, we return to the previous state without data loss.
  7. Handover of code and documentation. We hand over the repository with change history, a description of hooks and fields, instructions for updating and rolling back, and configuration notes. Your engineers can maintain the module further without us.

Each stage ends with an artifact: a logic map, a module design, a set of test scenarios, documentation. This makes the code handover predictable — you get not a "black box" but a module whose structure is clear to your team.

How is a hook different from a custom Fieldtype, and when is a Process needed?

A hook is an interception of already existing behavior of the core or another module without modifying their code. It fires at a specific point in the lifecycle: before or after a page is saved, during query building, or when a template is rendered. A hook does not create new entities — it changes how the system processes already existing data.

If the task sounds like "when a page is saved, recalculate a related field" or "override the output in a listing," the extension point is a hook, and there is no need to build anything more elaborate.

A Fieldtype is responsible for a different layer: it introduces a new data type with its own storage schema, input form in the admin panel, and validation rules.

This is the choice when the built-in types are not enough — for example, you need a field with a composite value that is stored not as a string but as a structure, and it must behave correctly in queries, sorting, and search.

A Fieldtype brings with it an Inputfield for the interface and, if necessary, its own storage page type. This is the heaviest of the three options: an error in the storage schema manifests on all pages with that field, and migrating the data later is expensive.

A Process creates a separate administrative section — its own page in the admin panel with a list, forms, and actions.

It is needed when the task has a standalone interface: importing from an external source, bulk operations on the page tree, a settings panel that is not tied to a single page.

A Process does not change the behavior of existing data directly — it gives the operator a tool that creates or modifies that data.

Choosing an extension point is a matter of choosing the minimal sufficient intervention. First we check whether a hook can handle the task; if a new data type is needed — a Fieldtype; if a separate screen for working with data is needed — a Process.

In practice, a module's architecture often combines all three: a Process provides the admin section, a Fieldtype stores the structure, and hooks connect them to the rest of the system.

This separation keeps each layer testable in isolation and prevents the module from turning into a monolith that is scary to update.

Case study: how a custom module eliminated manual work and a class of failures

Before implementation, a catalog of several thousand entries was maintained by hand: a content manager opened each record, transferred values from the supplier's spreadsheet into fields, and manually assigned categories and relationships between products.

For a single batch this took several hours, and copies of the same entry diverged in structure — some fields filled, some empty, somewhere a category specified as text instead of a reference.

The errors were predictable and of the same type: duplicate entries, broken relationships, fields with the wrong data type. Each such record broke queries and output — the page either failed to open or showed an empty block. Finding and fixing them took more time than the upload itself.

What was changed

We built a custom ProcessWire module that covers the entire import path inside the admin panel. A hook on page save checks field types and required values before writing to the database — an invalid entry simply isn't saved, and the manager sees which field needs correcting.

A custom Fieldtype stores a composite set of attributes as a single value, so relationships and categories no longer fall out of sync with each other.

A separate Process adds a batch upload screen to the admin panel: the supplier's file is parsed, matched against existing entries by external identifier, updates matches, and creates only new records. Re-importing the same file doesn't spawn duplicates — the operation is idempotent.

Metrics before and after

Indicator Before After
Manual operations per batch hundreds one file upload
Batch publication time several hours minutes
Duplicates and broken relationships regularly class of errors eliminated
Rolling back a failed import manual editing return to the previous state

The key effect is not speed itself, but the eliminated class of failures: invalid types and duplicates are cut off at the input rather than discovered a week later in the output.

The validation and matching logic is built on the core's standard API ProcessWire, so updating the system doesn't break the module.

The content manager now spends time checking the result rather than transferring values. If you have a similar manual process — describe the task, and we'll come back with an estimate and a plan.

Compatibility, rollback, and access: what to discuss before the start

A custom ProcessWire module lives inside the core, so the first thing we check before the start is the module's compatibility with the current ProcessWire branch and the PHP versions on your servers.

If the project is on an LTS branch and the hosting runs an old interpreter, some of the API may behave differently: hooks attach, but behavior changes on edge-case calls.

That is why we lock down a matrix of "core version / PHP version / hooks and Fieldtype in use" and run it on a copy of your environment, not on a clean build.

What if a core update breaks the module? We do not patch the core — the module is installed as a separate package and connected through the standard mechanism.

Before rollout, we prepare a safe rollback: a database snapshot, a backup of the module files, and a return point to the previous release. Rollback is a single operation, not a manual restore from an archive, so the downtime window is measured in minutes.

How are permissions separated? We describe the access levels right away: roles in the admin panel, permissions on Process pages, restrictions on fields and write operations.

Separately, we agree on who on your team gets access to module management and who gets access to content only, so that editing settings does not remain with a single person.

What will be included in the documentation handover?

A module diagram with hook connection points, a description of the Fieldtype and Process pages, a compatibility matrix, instructions for rollback and role separation. Plus a core update policy — what to check before switching branches so the module does not break.

What you receive:

  • module source code with a change history;
  • a diagram of hook connections and custom field types;
  • a compatibility matrix with core and PHP versions;
  • instructions for safe rollback and backup;
  • a description of access levels and roles in the admin panel;
  • a policy for updating the core without losing module functionality.

Describe your current configuration — core version, hosting, and module tasks. We will come back with an estimate, a work plan, and a list of what we will need from your side to access the environment.

Our competencies:

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1393
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1368
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1139
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1152
  • Website development for SBH Partners
    Website development for SBH Partners
    1231
  • Website development for Red Pear
    Website development for Red Pear
    641