Ask A Question

Notifications

You’re not receiving notifications from this thread.

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

TL asked in Rails

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?

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 81,842+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more.

    © 2024 GoRails, LLC. All rights reserved.