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: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.6 2026-09-17 20:57 UTC

This package is auto-updated.

Last update: 2026-09-18 13:56:02 UTC


README

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

VueForge for October CMS

Tests License: MIT Version 0.1.7

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

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, for building the frontend assets (Vite 5/6, Vue 3.4+, TypeScript)

Quickstart

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

  2. Build its frontend assets once:

    cd plugins/amjad/vueforge
    npm install
    npm run build
  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

    The field's underlying model attribute should be a JSON-castable column (protected $jsonable = ['tags', 'metadata']; on the model, or a native JSON column).

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: Amjad.VueForge (matches the registered October CMS Marketplace author code exactly - confirmed by a real rejected submission: "Supplied plugin code 'AmjadIqbal' does not match author code of 'Amjad'").

Not published to Packagist.org - the amjad vendor there is already taken by an unrelated package, and publishing under a different vendor would install this plugin at the wrong directory (composer/installers derives the install path only from the Composer package's own vendor prefix, with no override) and silently fail to load, since October's plugin loader then looks for a class that wouldn't exist at that path. This doesn't affect distribution through October CMS itself:

Via the October Marketplace (once the listing is live):

php artisan plugin:install Amjad.VueForge

Manual install (works today, no Marketplace approval needed):

  1. Clone this repository directly into your October CMS application's plugins/amjad/vueforge directory (i.e. this repo's root becomes that directory - do not nest it any further).
  2. cd plugins/amjad/vueforge && npm install && npm run build.
  3. php artisan october:migrate (no migrations ship with this plugin, but this refreshes the plugin registry so the widget/console command appear).

composer.json's package name (amjad/vueforge-plugin) is retained for October's own internal package metadata but is not registered on public Packagist.org - see CHANGELOG.md (0.1.7) for the full reasoning if you're wondering why composer require isn't listed as an install method here.

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 not committed

The built Vite output (assets/dist/, including manifest.json) is .gitignored. ViteResolver requires a real build to resolve production assets, so run npm run build after installing this plugin - there is no built-in fallback. This mirrors how most modern Vite-based October plugins in the ecosystem ship (source in git, build as an install step), and avoids committing machine-generated, hashed filenames that differ per Node/Vite version.

License

MIT - see LICENSE.