Search by

amjadiqbal / vueforge-plugin

amjadiqbal

The rapid Vue 3 component & widget engine for October CMS backend interfaces.

Package info

github.com/amjadiqbal/oc-vueforge-plugin

Type:october-plugin

pkg:composer/amjadiqbal/vueforge-plugin

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 5

v0.1.11 2026-10-11 07:50 UTC

This package is auto-updated.

Last update: 2026-10-11 08:15:44 UTC


README

VueForge — the rapid Vue 3 component & widget engine for October CMS

VueForge for October CMS

The rapid Vue 3 component & widget engine for October CMS backend interfaces.

Latest Version Tests PHP Version License

A VueForge TagInput widget, live in a real October CMS backend form

VueForge lets you drop a real Vue 3 .vue single-file component (Composition API, <script setup>, TypeScript, compiled by Vite) into any October CMS backend form as a FormWidget - with prop serialization, JSON save/load, and a bi-directional v-model-style sync to the form's native HTML submit already wired up. It is a deliberate alternative to October's own native VueComponentBase system - see docs/VUE3_MIGRATION_GUIDE.md for how the two differ and when to use which.

Requirements

  • October CMS 4.2+ (built and tested against 4.4.5, october/rain ^4.4)
  • PHP 8.2+
  • Node 18+ / npm, only if you build your own Vue components (Vite 5/6, Vue 3.4+, TypeScript) - the plugin itself ships pre-built

Quickstart

  1. Install the plugin into plugins/amjadiqbal/vueforge (see the Installation section below).

  2. Nothing to build - the compiled assets ship in assets/dist/. (You only need npm install && npm run build after adding your own Vue components.)

  3. Use the vueforge widget type in any fields.yaml:

    tags:
        label: Tags
        type: vueforge
        component: TagInput
        props:
            placeholder: "Add a tag..."
    
    metadata:
        label: Metadata
        type: vueforge
        component: JsonEditor

    Works on any model attribute: a JSON-castable column (protected $jsonable = ['tags', 'metadata']; or a native JSON column), or a plain text column such as a Tailor blueprint field, where the value is stored as a JSON string and decoded when the form loads.

That's it - TagInput (array state) and JsonEditor (nested key/value object state) ship as working examples in assets/vue/components/.

Architecture

formwidgets/VueWidget.php     FormWidgetBase subclass: renders the mount
                              point, serializes props, sanitizes save data.
classes/ViteResolver.php      Resolves the right asset (dev server or built
                              manifest.json chunk) for a widget's Vite entry.
assets/js/vueforge.ts         ESM hydrator: finds [data-vueforge-widget]
                              elements, dynamically imports and mounts the
                              matching component.
assets/js/composables/
  useVueForge.ts              Bi-directional v-model <-> hidden <input> sync.
  useOctoberAjax.ts           Typed wrapper around October's window.jax AJAX API.
assets/vue/components/
  TagInput.vue                Example: array state (chips/tags).
  JsonEditor.vue               Example: nested key/value object state.
console/MakeVueWidget.php     `php artisan vueforge:make` generator.

How a value round-trips

  1. Load: VueWidget::prepareVars() reads the model attribute via October's normal getLoadValue(), JSON-encodes it (HTML-attribute-escaped
    • see the migration guide, item 9), and renders it into data-vueforge-props plus a seed <input type="hidden" value="...">.
  2. Hydrate: vueforge.ts finds the mount point, dynamically imports the named component, and mounts it with the parsed props.
  3. Edit: the component uses useVueForge() to get a reactive value ref; every change re-serializes it into the hidden input's value.
  4. Save: October's normal form submit posts the hidden input's JSON string; VueWidget::getSaveValue() parses and sanitizes it (rejecting malformed JSON and stripping anything that isn't plain array/scalar data) before Eloquent persists it.

YAML field configuration reference

Key Type Default Description
component string JsonEditor The Vue component to mount. Must be registered in vueforge.ts's component map (or via registerVueForgeComponent() for your own components).
viteEntry string assets/vue/components/{component}.vue Source path used to look up the built asset in Vite's manifest.
props array [] Extra static props merged with the field's current value (passed as modelValue) before being handed to the component.

Artisan CLI: scaffolding your own widget

php artisan vueforge:make Acme.Blog TagList

Generates:

  • plugins/acme/blog/formwidgets/TagList.php - a VueWidget subclass with $component/$viteEntry pre-filled.
  • plugins/acme/blog/assets/vue/TagList.vue - a <script setup lang="ts"> stub already wired to useVueForge().

and prints the YAML type: code to use. Register the generated FormWidget in your plugin's Plugin::registerFormWidgets() as usual.

Note: the generated component imports useVueForge via a relative path across plugin folders. This only resolves in your own plugin's Vite build if its dev server is configured with server.fs.allow including the VueForge plugin's directory (Vite restricts serving files outside its project root by default) - see docs/VUE3_MIGRATION_GUIDE.md.

Installation

Plugin code: AmjadIqbal.VueForge. Composer package: amjadiqbal/vueforge-plugin

  • same vendor identity as every other package from this author (GitHub, Packagist, WordPress.org). This plugin is not eligible for an October CMS Marketplace listing under this account - October's Marketplace author code for this account is a separate, permanent identity (Amjad, confirmed by a real rejected submission), and a plugin's Composer vendor and its Marketplace author code cannot diverge without breaking composer require for anyone who installs it (composer/installers derives the install path only from the Composer package's own vendor prefix, with no override - a mismatch means October's plugin loader looks for a class that doesn't exist at that path and the plugin silently never loads). See CHANGELOG.md (0.1.7) for the full reasoning and the history of this decision.

Via Composer (once published to Packagist):

composer require amjadiqbal/vueforge-plugin

Manual install:

  1. Clone this repository directly into your October CMS application's plugins/amjadiqbal/vueforge directory (i.e. this repo's root becomes that directory - do not nest it any further).
  2. No build step is needed - the compiled assets ship in assets/dist/. If you upload the plugin by FTP or zip instead, make sure that folder comes with it.
  3. php artisan october:migrate (no migrations ship with this plugin, but this refreshes the plugin registry so the widget/console command appear).

Testing

  • PHP: vendor/bin/phpunit from your October application root, scoped to this plugin's tests/ directory (see the docblock in tests/VueWidgetTest.php for the exact invocation and why a bare Backend\Classes\FormField + VueWidget pair is used instead of a full Backend\Widgets\Form).
  • TypeScript: npm run typecheck (vue-tsc --noEmit).
  • JS component behavior: npm test (Vitest + @vue/test-utils + jsdom) - covers mount/prop-hydration/hidden-input sync/unmount for both example components.
  • Build: npm run build (Vite production build; fails the whole build script if vue-tsc --noEmit reports errors first).

Why assets/dist/ is committed

The built Vite output (assets/dist/, including manifest.json) is committed on purpose. Composer, Packagist and the October Marketplace install straight from the repository and run no build step, so a plugin that relies on npm run build fails on every customer install with "Vite manifest not found" (this is exactly what 0.1.1 - 0.1.9 did). The manifest sits at assets/dist/manifest.json, not Vite's default hidden .vite/ folder, because FTP and zip uploads often drop hidden folders.

If you change assets/js or assets/vue, run npm run build and commit the result: CI fails when the committed assets/dist/ differs from a fresh build, and a PHPUnit test renders the widget and checks every asset it registers exists on disk.

License

MIT - see LICENSE.md.

Support

Need this customised, or something built?

I'm available for custom development, package integration, and technical consulting.

Hire me on Upwork Discord

More packages

Part of a family of open-source packages — see all of them.

oc-blockcraft-plugin The modern TipTap block editor & document builder for October CMS
laravel-tiptap Tiptap editor for Laravel, backend-driven config and secure uploads
kiln Deploy-time OPcache control for Laravel
laravel-logpulse Log health monitoring & intelligent alerting for Laravel