Skip to content

Forms in your theme (sync)

You can keep form definitions in your theme’s repository as JSON files and import them into a site. This is similar to ACF’s Local JSON, with one firm rule: it is sync-first. At runtime the database is always the source of truth. The files are a versioned developer artifact, and the runtime never reads them.

That distinction matters. All validation, including the safety check on webhook URLs, runs when a config is saved to the database. It does not run on a file sitting in your repo. Import is the only path in.

Put one file per form at:

{theme}/data/octa-forms/{slug}.json

Both the child and parent theme are scanned, and the child wins for a given slug. The file name is the form’s slug, and it must equal sanitize_title(name).

Each file is an envelope with a title and the config:

{
"title": "Contact",
"config": {
"fields": [ /* … */ ],
"email": { /* … */ },
"settings": { /* … */ }
}
}

The config is exactly the shape stored in the database, without title (that comes from the envelope).

In the admin, go to OctaForms → Developer and use the theme sync section. It shows a status table with Import and Import all buttons.

On a deploy, use WP-CLI (see WP-CLI):

Terminal window
wp octa-forms theme status
wp octa-forms theme import --all # exits non-zero on any failure, good for CI
wp octa-forms theme import --slug=contact
wp octa-forms theme import --all --force # also overwrite conflicts

Status is detected by comparing a hash of the config, not by file dates:

Status Meaning
New No form for this slug yet. Import will create it.
In sync File and database match.
File newer The file changed. A safe import.
Database changed Edited in the admin since the last import.
Conflict Both sides changed. Import overwrites the database, with a one-step undo saved.
Not linked A form with this slug exists but was created independently of the file.
Database only No file. Nothing to import.

Import never deletes anything. Deleting a file leaves the form in the database untouched.

On a local or development environment, and only if the directory {child theme}/data/octa-forms/ exists, every save in the form editor also writes the file back. That closes the loop: edit on local, get the file in your repo, commit, deploy, import on production.

Export is off on production, because writing into a theme at runtime is an anti-pattern. Control it with the octa_forms_theme_export_enabled filter.

  • octa_forms_theme_form_dirs — the directories scanned for files.
  • octa_forms_theme_import_status — the status given to forms created by an import (defaults to publish).
  • octa_forms_theme_export_enabled — the gate for local export.

See Hooks and filters.