Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

fylzero's avatar
Level 67

How to get Vite assets working on Vapor?

I'm struggling a bit to understand how this needs to work / can't seem to find anything current tutorial-wise / the docs are not clear to me how to do this.

Does anyone have an example of a working app.js that gets the asset helper working in Vapor when using Vite? The app I'm trying to get going on Vapor is Laravel 9/Jetstream-based (if that is useful to know).

In the docs here: https://docs.vapor.build/1.0/projects/deployments.html#assets

It says...

Vapor With Laravel Vite

 

If you are using Laravel Vite with your project, you only need to utilize the asset helper when you are referencing assets you don't want bundled, such as those that already live in your public directory.

 

If you want to use the asset helper with your Vite project, you will also need to specify the base URL for assets in your application's entry point, for example in your resources/js/app.js, like so: Vapor.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL)

  • I'm not sure what the implications are here. Is it preferable when using Vite to bundle the assets (which doesn't seem to be happening since the images are broken)?
  • Shouldn't they ideally use the asset helper and live on Cloudfront?
  • The example provided above for VITE_VAPOR_ASSET_URL doesn't say what Vapor.withBaseAssetUrl is referencing (is that a reference to window.Vapor?).

In Vue 3 this is what I was assuming but doesn't seem to work...

// Laravel Vapor Asset Helper
import { VaporInstance } from 'laravel-vapor'
VaporInstance.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL)
window.Vapor = VaporInstance

I'm getting Uncaught TypeError: Cannot read properties of undefined (reading 'withBaseAssetUrl') no matter how I seem to try this.

Thanks in advance!

0 likes
11 replies
fylzero's avatar
Level 67

Gave up on this for a while - this is my first Vapor project.

Found this in the Laravel docs - this works, but I'm still not sure if this is preferable to using the asset helper. I would imagine the asset helper would be better if it can host images via Cloudfront. https://laravel.com/docs/9.x/vite#url-processing

collinped's avatar
Level 3

@fylzero Ended up running into this myself but was able to solve it with the below which sends the assets to ClouddFront:

images get placed in the /resources directory - IE: /resources/img/image.png

// .env - NOT NEEDED IN YOUR VAPOR ENV VARIABLES (Automatically Injected)
VITE_VAPOR_ASSET_URL="${APP_URL}" 
// app.js
import Vapor from 'laravel-vapor'
Vapor.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL)
window.Vapor = Vapor
// app.js when using VUE/Inertia.js
.mixin({
	methods: { asset: window.Vapor.asset }
})
// Note that there is no slash before /img - CRITICAL
<img :src="asset('img/image.png') />
4 likes
stgbuc's avatar

@collinped could you please go more into depth? I tried your approach but I still get the:

"Unresolved function or method asset() "

import './bootstrap';
import '../css/app.css';

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from '@inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import Vapor from 'laravel-vapor'


const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';

Vapor.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL)

window.Vapor = Vapor


createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .use(ZiggyVue, Ziggy)
            .mixin({
                methods: { asset: window.Vapor.asset }
            })
            .mount(el);
    },
});

InertiaProgress.init({ color: '#4B5563' });

And the component

<template>
  <div>
    <Head title="Create Participant" />



    <h1 class="mb-8 text-3xl font-bold mt-2">
      <Link class="text-indigo-400 hover:text-indigo-600" href="/admin/particiapnts">Participants </Link>
      <span class="text-indigo-400 font-medium">/</span> Create
    </h1>
    <div class="max-w-3xl bg-white rounded-md shadow overflow-hidden">
      <form @submit.prevent="store">
          
        <img :src="asset('img/image.png') />
        
        <div class="flex flex-wrap flex-col -mb-8 -mr-6 p-8">
            <text-input v-model="form.name" :error="form.errors.name" class="pb-8 pr-6 w-full lg:w-1/2" label="Participant name" />
            <text-input v-model="form.bio" :error="form.errors.bio" class="pb-8 pr-6 w-full lg:w-1/2" label="Participant bio" />
        </div>
        <div class="flex items-center justify-end px-8 py-4 bg-gray-50 border-t border-gray-100">
          <loading-button :loading="form.processing" class="btn-indigo" type="submit">Create Contact</loading-button>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import { Head, Link } from '@inertiajs/inertia-vue3'
import Layout from '@/Layouts/SuperAdminLayout.vue';
import LoadingButton from '@/Shared/LoadingButton.vue';
import TextInput from "@/Shared/TextInput.vue";

export default {
  components: {
    Head,
    Link,
    TextInput,
    LoadingButton,
  },
  layout: Layout,
  props: {},
  remember: 'form',
  data() {
    return {
      form: this.$inertia.form({
        name: '',
        bio: '',
      }),
    }
  },
  methods: {
    store() {
      this.form.post('/admin/participants')
    },
  },
}
</script>
stgbuc's avatar

my solution

import Vapor from 'laravel-vapor'
import { createApp } from 'vue/dist/vue.esm-bundler';

const app = createApp({});

Vapor.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL)
window.Vapor = Vapor

app.mixin({
    methods: {
        asset: window.Vapor.asset
    },
});
devinfd's avatar

Hi There, I am struggling with the same issue.

  1. When images are in the /resources/img directory as @collinped described and when using the asset helper (ex: <img :src="asset('img/image.png') />) Vite does not bundle the images into the build.

  2. It seems to me that VITE_VAPOR_ASSET_URL is not injected by Vapor in production.

I feel like this should not be so difficult but I can't get assets/images to load in Vapor.

fylzero's avatar
Level 67

@devinfd Totally agree this shouldn't be so complicated! This really should be documented better in Vapor imo.

  1. Correct - Vite is not bundling here - the idea is that it should be sending the files to S3 or Cloudfront.
  2. As @collinped mentioned - VITE_VAPOR_ASSET_URL is not needed in Vapor environment variables.
fylzero's avatar
Level 67

@collinped I did everything you suggested and it seemed to get me a bit further - but all my Cloudfront links say they are "Access Denied". Did you run into this as well? I haven't tried messing with Vapor since last testing this 10 months ago, currently trying again and assets seem to be the biggest blocker for me.

fylzero's avatar
Level 67

Oddly, one header image works on my login page but once I login, none of the other images will load. Seems like maybe a ACL/origin type issue? idk, still currently lost.

fylzero's avatar
Level 67

Got a bit further - realized when Vapor is looking for image assets to upload to S3/Cloudfront - that it seems to only be parsing Blade files, not Vue files. Still trying to work this out but at least happy to know that is the problem. Getting closer.

fylzero's avatar
Level 67

Got it figured out. Turns out Vapor looks to the /public directoy for images to move to S3. Having the images in /resources is what made this break for me.

If you run into this, do everything the same as @collinped suggests but just move your images to /public/images

Please or to participate in this conversation.