Download the PHP package mediagone/vue-in-twig-bundle without Composer

On this page you can find all versions of the php package mediagone/vue-in-twig-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package vue-in-twig-bundle

mediagone/vue-in-twig-bundle

Latest Stable Version Total Downloads

Integrates Vue.js 3 into Twig/Symfony templates and extends Vue's capabilities with Twig's server-side power: slots, extends, embed...

Compose your components, inject PHP constants or initial data directly into them and generate safe Symfony URLs with dynamic parameters.

No Node.js toolchain required: no bundler, no build step, no node_modules...

The primary objective is to automate and simplify using Vue inside a Symfony/Twig app, without adding a JS build toolchain on top of it.

Table of contents


Installation

This package requires PHP 8.1+, Twig 3 and "symfony/framework-bundle" ^6.1|^7.0

  1. Add it as Composer dependency:

  2. Register the bundle in config/bundles.php:

  3. Load Vue 3 full build (with compiler) in your layout. \ Note: the compiler build is required since there is no precompile step, the x-templates are compiled in the browser at runtime.

  4. A few components also call API endpoints via axios — load it once if you use any of these: ToggleButton, UploadZone, DataList, DataEditor

Introduction

This bundle formalizes a specific integration pattern: Vue components are written as x-templates, rendered and composed server-side by Twig.

PHP as the single source of truth

Beyond simplifying the front-end toolchain, the core benefit of rendering Vue server-side with Twig is that PHP stays the single source of truth, automatically kept in sync with the front-end.

Server-side values — enum cases, constants, config, URLs — flow into the Vue UI at render time, so there is no hand-maintained JS duplicate that silently drifts out of sync when the PHP changes.

A concrete example: a <select> populated by iterating a PHP enum's cases in Twig.

Add, rename or remove a case in BlockType, and the dropdown updates on the next render — no parallel JS array to keep in sync. \ The same idea applies to v-if checks against a status, a list of allowed types, a feature flag, etc. (see Injecting PHP constants into Vue expressions below).

What Twig brings that Vue alone cannot do:


Get started

Everything is wired from your layout via Twig tags and functions — there is no .js entry file to write by hand.

Use {% vue_app %} to create and mount automatically your Vue application:

Declare required components to be queued for inclusion with the {% vue_use %} tag:

Every {% vue_use %} tag must be used within the {% vue_app %} tags — whether placed in the same template or in any included or extended template:


Example:

Layout.twig:

Page.twig:

Partial.twig:

Placed in your base layout, vue_app will output:

  1. Opening tag<script>window.VUE_APP = Vue.createApp(window.VUE_ROOT ?? {});</script> + setup.js (delimiters, global mixin)
  2. Body → rendered normally; {% vue_use %} tags queue components silently (no output)
  3. Closing tag → all queued component templates + scripts (deduplicated, in call order) + <script>VUE_APP.mount('selector');</script>

Differences from standard Vue.js

Delimiters

Vue's default {{ }} delimiters conflict with Twig, so Vue-in-twig reconfigures them to [[ ]]. \ Use [[ ]] everywhere Vue reactivity is needed — in x-templates (.vue.twig) and in the mounted HTML.

The two template engines coexist in the same markup, each running at a different time:

Engine Runs Syntax
Server Twig At request time (PHP) {{ }}
Client Vue In the browser (JS) [[ ]]

Twig composition over Vue slots

Components in this bundle are extended/composed with Twig ({% embed %} + blocks — server-side composition) rather than Vue's slots, because they are too limited for and offer less customization. This is why a complex component like DataList exposes Twig blocks instead of Vue slots for its markup — see the DataList example.

Simple, purely visual customization points (a button label, a small fragment of markup) still use regular Vue slots — e.g. Modal's header/footer, DropZone's instructions/infos...

Injecting server-side data into Vue props

Twig {{ }} still works inside HTML attributes — Twig renders the attribute value as a string, and Vue reads it as a JS expression:

Both lines above write the value raw, with no JSON encoding — safe here given the nature of these values: app.request.locale and maxFileSizeBytes can't contain characters that would break the expression. For anything else (user input, free text, structured data...) this library provides the |vue_json_encode filter, which is an HTML-safe replacement for |json_encode that serializes and escapes the value safely:

_Note: vue_json_encode applies JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_THROW_ON_ERROR._

Injecting PHP constants into Vue expressions

Constants and enums can be injected directly into Vue attribute values — Twig renders them as literal strings before Vue compiles the template.

Generate safe URLs for Symfony's routes

Symfony URLs combining static and dynamic, Vue-side parameters can be safely generated via the vue_path(route, staticParams, dynamicParams) function (similar to Symfony's path()):

Generates: '/ajax/account?accountId=__ACCOUNTID__'.replace('__ACCOUNTID__', account.id)

File naming convention

.vue.twig + .vue.js — immediately identifies Vue files among other Twig templates.

Placed in your base layout, it'll output:

  1. Opening tag<script>window.VUE_APP = Vue.createApp(window.VUE_ROOT ?? {});</script> + setup.js (delimiters, global mixin)
  2. Body → rendered normally; {% vue_use %} tags queue components silently (zero output)
  3. Closing tag → all queued component templates + scripts (deduplicated, in call order) + <script>VUE_APP.mount('selector');</script>

VUE_ROOT — root component options

Vue.createApp() is called with window.VUE_ROOT ?? {}. Declare root-level data()/methods/etc. globally before {% vue_app %} runs:

Tip — mount placement

Vue replaces the mount target's content (container.innerHTML = '') before mounting. Since {% endvue_app %} outputs the queued x-templates and component scripts, place {% vue_app %}...{% endvue_app %} after the element you mount onto (e.g. after </div> closing #App), not inside it — otherwise the x-templates get wiped out before Vue can read them.

Declare and include components (2)

Vue components are declared and queued for inclusion via the {% vue_use %} tag:

Can be called from any partial, before {% endvue_app %}. Duplicate calls are ignored (include-once); the queue otherwise preserves call order. Each call queues two files (if they exist):

By default, a bare 'Category/Name' resolves against the bundle's own @VueInTwig namespace — this is what you use for every built-in component shown in Examples.

Registering your own components

By default, a bare 'Category/Name' resolves against the bundle's own @VueInTwig namespace — this is what you use for every built-in component shown in examples.

{% vue_use %} also accepts an explicit @Namespace/... reference, used as-is instead of being prefixed. This lets a consuming app register its own Vue components through the same queue/dedup mechanism — typically to extend a bundle component (see the DataList example).

Without this config, the default namespace stays @VueInTwig, so every bare {% vue_use 'Category/Name' %} keeps resolving to the bundle's own components — no change for the common case.

Order matters. The queue is flushed in call order, immediately before VUE_APP.mount(). A component that extends another (e.g. VUE_APP.component('vue-datalist')) must be {% vue_use %}'d after its base — otherwise the base isn't registered yet when the extending component reads it.

Override the default configuration

The default configuration can be overridden via the vue_config function or tag, which populates window.VUE_CONFIG — read by setup.js and by components such as DataList. Two complementary forms:

The dot-path maps to VUE_CONFIG.root.key ('search.debounceMs'VUE_CONFIG.search = { debounceMs: 500 };). Both forms write the same way and can target the same root key from different places; the buffered config is flushed once as a single <script> block at {% endvue_app %}, replacing the old pattern of an inline <script>VUE_CONFIG.x = ...;</script> override placed by hand in the body.

setup.js mixins and helpers

setup.js is rendered automatically by the opening {% vue_app %} tag. It provides:

VUE_APP.config.compilerOptions.delimiters Set to ['[[', ']]']
format_date(value, locale, options) Global mixin method — Intl.DateTimeFormat wrapper
month_name(month) Global mixin method — localized month name for a 1-12 month number
slugify(str) Global mixin method — ASCII slug
window.debounce(fn, ms) Helper used internally by DataList; overridable via ??=
window.VUE_CONFIG Defaults to { debounceSearch: 300 }, overridable via vue_config

Examples

Each example assumes the component was declared with {% vue_use %} and Vue 3 (+ axios where noted) is loaded, as described in Get started. Props tables list every prop declared on the component; "Required" props have no default.

Controls

DatePicker (vue-date-picker)

Date selection input (year / month / day selects).

Props

Prop Type Default Required
initialDate Date
yearsBefore Number 2
yearsAfter Number 3
yearsList Array null (computed from yearsBefore/yearsAfter)

Emits: dateSelected (the new Date)

DatetimePicker (vue-datetime-picker)

Date + time selection input.

Props

Prop Type Default Required
initialDate Date
useTime Boolean true
showAllMinutes Boolean false (otherwise rounded to 5-minute steps)
yearsBefore Number 2
yearsAfter Number 3
yearsList Array null
futureDateText String ''
pastDateText String ''

Emits: dateSelected (the new Date)

DropZone (vue-drop-zone)

File selection + validation + a confirmation preview modal. Does not upload — it only emits the selected files; the parent handles the actual upload (see UploadZone for an integrated alternative).

Props

Prop Type Default Required
title String
selectionLimit Number 0 (unlimited)
fileMaxSize Number 0 (unlimited), in bytes
fileMimeTypes String '' (any), comma-separated

Emits: select (array of valid File objects, once confirmed in the preview modal)

Slots

Slot Scope Description
instructions fileInput Replaces the default "drag & drop or browse" text
infos formats, maxSize Replaces the default formats/size hint

UploadZone (vue-upload-zone)

File selection with an integrated upload (axios) and an optional built-in crop step (embeds ImageCropper) before sending.

Props

Prop Type Default Required
postUrl String
postParameterName String
title String
dropText String
allowedFileTypes String '', comma-separated
allowMultipleFiles Boolean false
maxFileSize Number 0 (unlimited), in bytes
dropInfoText String '({formats} <= {maxSize})' — placeholders: {formats}, {outputWidth}, {outputHeight}, {maxSize}
editorEnabled Boolean false — crop step for a single image (png/jpeg) before upload
editorOutputWidth / editorOutputHeight Number 0 (natural crop size)
editorOutputFormat String '' (same as source)
editorFixedRatio Boolean false
sendButtonLabel / cancelButtonLabel / okButtonLabel String 'Envoyer' / 'Annuler' / 'OK'
fileTooLargeTitle / fileTooLargeText String text supports {filename}, {size}, {maxsize}
uploadFailureTitle / uploadFailureText String shown if the axios POST fails

Emits: uploaded (the server response's results)

ImageCropper (vue-image-cropper)

Interactive crop UI (8 resize handles + move) over a source image, rendering the crop to a <canvas>.

Props

Prop Type Default Required
sourceDataUrl String
outputWidth / outputHeight Number 0 (natural crop size)
outputMimeFormat String '' (same as source)
fixedRatio Boolean false (hold Shift while dragging to force it ad hoc)

Emits: cropped (a Blob, from canvas.toBlob())

SwitchButton (vue-switch-button)

Toggle switch that is parent-controlled: it never mutates the bound object itself, it only asks for the change.

Props

Prop Type Default Required
object Object
property String
valueOn String\|Boolean\|Number
valueOff String\|Boolean\|Number
disabled Boolean false

Emits: switch-request (the would-be next value — the parent decides whether/how to apply it, e.g. after an API call)

ToggleButton (vue-togglebutton)

Two-state button that is API-driven, unlike SwitchButton: it fetches its own current value on creation and posts the change itself.

Props

Prop Type Default Required
api_url String
result_name String
result_property String
value_on / value_off String '1' / '0'
confirm_on / confirm_off String '' (no confirmation)
disabled Boolean false

On creation, performs GET {api_url}?fields={result_property} and reads response.data.results[result_name][result_property]. On click, POSTs the new value the same way and updates from the response. No emits — state lives in the component.

Layout

Modal (vue-modal)

Props

Prop Type Default Required
titleText String ''
titleStyle String '' (e.g. 'warning', 'danger')
yesButtonText / noButtonText String '' (hidden if empty)
yesButtonClass / noButtonClass String '--primary' / ''
yesButtonEnabled / noButtonEnabled Boolean true

Emits: clickyes, clickno (from the default footer buttons)

Slots

Slot Description
header Replaces the default title block
(default) Modal body
footer Replaces the default yes/no buttons (you then own the emits)

LockWrapper (vue-lock-wrapper)

Locks/unlocks its content (e.g. a disabled form until the user explicitly unlocks it).

No props. Internal state: locked (defaults to true).

Slots

Slot Scope Description
content locked, lock, unlock, toggle The protected content
button locked Label/icon of the lock toggle button (the <button> itself, already wired to toggle(), wraps this slot)

Behaviors

Renderless components (no wrapper element) — they apply behavior directly to their single child.

AutoResize (vue-auto-resize)

Resizes its child (e.g. a <textarea>) to fit its content, on input and on window resize. No props, no emits — wraps exactly one child element.

Draggable (vue-draggable)

Native HTML5 drag & drop, zero dependency. Reorders a list's children and moves items between lists sharing the same group.

Props

Prop Type Default Required
modelValue Array ✓ (use with v-model)
group String null — two lists with the same non-null group accept moves between them
sort Boolean true — reorder within this list
emptyHeight String null — inline min-height forced while empty, so it stays droppable without CSS
usePlaceholder Boolean false — gap placeholder instead of the default thin insertion line

Emits: update:modelValue (new array), change (no payload) — the component mutates nothing in place, it re-emits new arrays.

Drop feedback is themable via CSS variables (on the list element or :root): --vue-draggable-indicator-color (#2684ff), --vue-draggable-indicator-size (2px), --vue-draggable-indicator-style (solid, line mode only), --vue-draggable-placeholder-bg.

Widgets

DataEditor (vue-data-editor)

Formalizes an inline-edit pattern: tracks whether item changed since it was loaded/saved, and shows a save bar only when there's something to save.

Props

Prop Type Default Required
item Object
postUrl String\|Function ✓ — a function receives item and must return the URL, for dynamic endpoints
postUrlProperties String ✓ — comma-separated list of item keys to send
resultPath String null dot-path into the response (e.g. 'results.portal') to sync item back from the server; omit to stay shape-agnostic
upToDateText String '' shown when there's nothing to save; empty keeps that panel hidden

No emits. Calling changed() (exposed in the default slot) re-checks whether item differs from its last-saved snapshot and shows/hides the save bar accordingly; save() posts the listed properties and re-snapshots on success.

Slots

Slot Scope Description
save-text Replaces the default "you have unsaved changes" text
(default) item, originalItem, data ($data), props ($props), changed The editable form
modal-error data ($data), props ($props) Replaces the default error message in the failure modal

DataList (vue-datalist)

Formalizes a list pattern: fetch + pagination + create/delete, with debounced refresh for search/filter inputs. vue-datalist itself is logic only — it has no template and no slots. The markup comes from embedding Widgets/DataList.twig and overriding its Twig blocks; your own component then extends the base logic.

1. Base component — queue it like any other:

2. Your extending component — registered in your own app, under your configured namespace so it loads through the same queue, after the base:

3. The markup — embed the bundle's template, overriding only the blocks you need:

Props (on the base vue-datalist, inherited by your component)

Prop Type Default Required
itemsListUrl String
itemsCreateUrl String '' (create disabled)
itemsDeleteUrl String '' (delete disabled), use a -ID- placeholder
page Number 1
config Object {} per-instance override, see below

Twig blocks (Widgets/DataList.twig)

Block Default Notes
TOOLS_LEFT / TOOLS_RIGHT empty / refresh button Toolbar content
TABLE_HEADERS empty <th> cells, inside the header <tr>
TABLE_ROW empty <td> cells for each row in items
TABLE_BUSY loading text Shown while isBusy
TABLE_EMPTY "no results" text Shown when items is empty
BODY empty Extra markup after the table (e.g. a "create" button)
MODAL_CREATE / MODAL_DELETE empty Body of the create/delete confirmation modals
MODAL_ERROR error description Body of the error modal

Overridable hooks (override in your extending component's methods)

Hook Purpose
parseResponse(response) Maps a successful list response to { items, page, pageCount, total }. Default reads response.data.payload; falls back to VUE_CONFIG.DataList.parseResponse if set
parseErrorResponse(response) Extracts { code, description } from a failed response. Falls back to VUE_CONFIG.DataList.parseErrorResponse
buildErrorModal(error, context) Builds the error modal object (title/description) from the extracted error; context is 'list'/'create'/'delete'
rowKey(row) :key for each row — defaults to row.id ?? row
rowAttributes(row) Extra attributes/listeners (e.g. onClick, class) merged onto each <tr>
modifyUrlParameters(params) Push extra query params (e.g. search/filters) before each list request
onItemsRefresh() / onItemsRefreshFailure() Called after a successful/failed refresh

Call this.debounceRefresh() (instead of this.itemsRefresh()) from a search/filter input handler to debounce the request using VUE_CONFIG.debounceSearch.

config shape{ parseResponse, parseErrorResponse, icons, texts, tooltips }, merged over VUE_CONFIG.DataList (global default for every list, set via vue_config); icons/texts/tooltips merge per-key, so a partial override keeps the other defaults.


All versions of vue-in-twig-bundle with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
symfony/framework-bundle Version ^6.1|^7.0
twig/twig Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package mediagone/vue-in-twig-bundle contains the following files

Loading the files please wait ...