amjadiqbal / vueforge-plugin
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
Requires
- php: >=8.2
- composer/installers: ^1.0 || ^2.0
- october/rain: >=4.2
Requires (Dev)
- phpunit/phpunit: ^10.0 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
VueForge for October CMS
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, only if you build your own Vue components (Vite 5/6, Vue 3.4+, TypeScript) - the plugin itself ships pre-built
Quickstart
-
Install the plugin into
plugins/amjadiqbal/vueforge(see the Installation section below). -
Nothing to build - the compiled assets ship in
assets/dist/. (You only neednpm install && npm run buildafter adding your own Vue components.) -
Use the
vueforgewidget type in anyfields.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
- Load:
VueWidget::prepareVars()reads the model attribute via October's normalgetLoadValue(), JSON-encodes it (HTML-attribute-escaped- see the migration guide, item 9), and renders it into
data-vueforge-propsplus a seed<input type="hidden" value="...">.
- see the migration guide, item 9), and renders it into
- Hydrate:
vueforge.tsfinds the mount point, dynamically imports the named component, and mounts it with the parsed props. - Edit: the component uses
useVueForge()to get a reactivevalueref; every change re-serializes it into the hidden input'svalue. - 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- aVueWidgetsubclass with$component/$viteEntrypre-filled.plugins/acme/blog/assets/vue/TagList.vue- a<script setup lang="ts">stub already wired touseVueForge().
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 breakingcomposer requirefor anyone who installs it (composer/installersderives 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). SeeCHANGELOG.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:
- Clone this repository directly into your October CMS application's
plugins/amjadiqbal/vueforgedirectory (i.e. this repo's root becomes that directory - do not nest it any further). - 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. 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/phpunitfrom your October application root, scoped to this plugin'stests/directory (see the docblock intests/VueWidgetTest.phpfor the exact invocation and why a bareBackend\Classes\FormField+VueWidgetpair is used instead of a fullBackend\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 wholebuildscript ifvue-tsc --noEmitreports 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
- 🐛 Bug or feature request — open an issue
- 💬 Questions — open an issue, or message me directly on Discord
- 🔒 Security issue — please report privately via GitHub's security tab
Need this customised, or something built?
I'm available for custom development, package integration, and technical consulting.
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 |
