Skip to main content

Webpacker: How do I use images inside my .vue single file components?

Rails • Asked by TL

We're using Rails and Webpacker with Vue single file components.

How do we insert images inside the <template> ? A far as I can see there are many options.

OPTION 1:
Use .vue.erb (with webpacker erb loader), and <% helpers.image_path %> as suggested in the official docs here.

Drawbacks: very slow to webpack compile for some reason (like 30 seconds on every single file change), AND it also breaks the linter. Also, still depends on Sprockets.

OPTION 2:
Add 'app/assets' to webpacker.yml resolved_paths array, as suggested in the official docs here, and then import the images directly in Javascript like the docs suggest:

import 'images/rails.png'

Drawbacks: to use this with Vue single file components, you kinda have to add it to the data or computed attributes, making them reactive. It's verbose, like so:

<template>
<img v-bind:src="railsImage">
</template>

<script>

import railsImage from 'images/rails.png';

export default {
  data() {
    return {
          railsImage,
    }
  }
}

OPTION 3:
Use require

<template>
  <img v-bind:src="require('images/rails.png')"
</template>

We can point to images/rails.png directly only if we add app/assets to the resolved_paths, otherwise you'll need to require ../../../app/assets/...

I thought this solution wouldn't use asset fingerprinting (the hash at the end of filename to help with cache invalidation), because it would be bringing the asset from Sprockets, but Webpacker does it automatically, creating a path like /packs/_/assets/images/rails-27695e0730a7fbf593ffbf5d8749024b.png

OPTION 4:
Our layman solution, which we use for some time in production for all 'staticData' I want to pass from Rails to Vue, using data-attributes. I use it not only for asset_paths, but for all data that is request-dependent (for example, my Rails app has internationalizaiton, so routes are dependent on request locale, that's why I can't use .erb in the javascript files for routes).

It works something like this. In the view:

<%= content_tag :div,
  nil,
  id: 'app_mount_point',
  'v-cloak': true,
  data: {
    'static-data': {
      railsImage: image_path('images/rails.png'),
            posts_path: posts_path,
    }
  }
%>

In the vue.app:

<template>
<img v-bind:src="staticData.railsImage">
<a v-bind:href="staticData.posts_path">See your posts in your language</a>
</template>
(...)
computed: {

    staticData() {
      return $(this.$root.$el).data('static-data');
    },

  },

This works great because staticData is a computed methods, which will be cached by Vue at load and wont be observed for changes unnecessarly.

It also helps that image_optim gem compresses all my asset images through Sprockets, so referencing them this way ensures I'm getting the compressed-processed image.

What option are you guys using? What would be the most idiomatic way of doing this?


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.