React_on_rails: [DOC PR WANTED]: How do I get my assets relative paths?

Created on 6 Jan 2017  路  18Comments  路  Source: shakacode/react_on_rails

Normally I would have a .jsx.erb file with something like <img src="<%= asset_path('spinner.gif') %>", I know asset_path is not going to work with Webpack but I've tried everything I could find here and elsewhere but nothing works. I found this in your help section but I'm confused at how you reference those symbolic links in JS - https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/rails-assets.md

Ideally I would like to keep our assets in the original Rails assets folder but if that's not possible then moving some over to the client folder is fine too.

up for grabs!

Most helpful comment

Here's what my webpack.config.js file looks like -

/* eslint comma-dangle: ["error",
  {"functions": "never", "arrays": "only-multiline", "objects":
"only-multiline"} ] */

const webpack   = require('webpack');
const path      = require('path');
const devBuild  = process.env.NODE_ENV !== 'production';
const nodeEnv   = devBuild ? 'development' : 'production';
const glob      = require('glob');
const isDebug   = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');

const config = {
  entry: [
    'es5-shim/es5-shim',
    'es5-shim/es5-sham',
    'babel-polyfill',
    './app/index.jsx',
  ].concat(glob.sync("./app/views/*/*/*", "./app/*/*/*")), // Allows you to use ReactOnRails.register({ }); in a components file rather than registering it in index.jsx

  output: {
    filename: 'webpack-bundle.js',
    path: '../app/assets/webpack',
    "publicPath": "/",
  },

  resolve: {
    extensions: ['', '.js', '.jsx'],
    alias: {
      react: path.resolve('./node_modules/react'),
      'react-dom': path.resolve('./node_modules/react-dom'),
      'ui_elements': path.resolve('./app/ui_elements'),
      'assets': path.resolve('./app/assets'),
      'vendor': path.resolve('./vendor'),
    },
  },

  plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jquery: "jQuery",
      "window.jQuery": "jquery"
    }),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(nodeEnv),
      },
    }),
    new webpack.ProvidePlugin({
      "React": "react",
    }),
  ],

  module: {
    loaders: [
      {
        test: require.resolve('react'),
        loader: 'imports?shim=es5-shim/es5-shim&sham=es5-shim/es5-sham',
      },
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
        loader: 'file-loader',
        query: {
          name: 'assets/[name].[ext]?[hash:8]',
        },
      },
      {
        test: /\.(mp4|webm|wav|mp3|m4a|aac|oga|png|gif)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          name: 'assets/[name].[ext]?[hash:8]',
          limit: 10000,
        },
      },
    ],
  },

};

module.exports = config;

if (devBuild) {
  console.log('Webpack dev build for Rails'); // eslint-disable-line no-console
  module.exports.devtool = 'eval-source-map';
} 
else {
  config.plugins.push(new webpack.optimize.DedupePlugin());
  console.log('Webpack production build for Rails'); // eslint-disable-line no-console
}

I can't remember exactly what fixed it but I'm pretty sure its going to assume your base route is rails_root/app/ and not in relation to the rails_root/client folder. If you don't see the image in your rails_root/app/assets/images/ folder then you need to figure out why that is first.

All 18 comments

You need to put the assets in the /client folder. Webpack will copy those to your bundles folder with fingerprints. The asset pipeline will then move them to your /public/assets folder, fingerprinting them again. React on Rails undoes the duplicate fingerprint with sym links.

BTW, webpack will only copy the assets if they are referenced. You need to either use background images or a require in your JS file.

BTW, webpack will only copy the assets if they are referenced. You need to either use background images or a require in your JS file.

This helped make it click for me, thank you. For anyone else coming behind me this is a simple example -

const spinner_URL = require('assets/images/spinner.gif');

const Foo = React.createClass({
  render: function() {
    return (
      <div>
        <h1>Hello</h1>
        <img src={spinner_URL} />
      </div>
    )
  }
});

export default Foo;

@ryangrush I am facing the same issue as you are. I can successfully get webpack to process the image and I get an "ok" URL after requiring it. However the image is not returned by the server later.

Could you share your webpack conf and image asset path where you are placing it?
Are you using a webpack image loader or file loader for this purpose?

My relevant conf:

    module: {
        loaders: [
            ... {
                test: /.*\.(gif|png|jpe?g|svg)$/i,
                loaders: ['file?hash=sha512&digest=hex&name=[name].[ext]', 'image-webpack']
            }
        ]
    },

Image located on client/app/assets/landing.jpg
After webpack process, I can effectively see it on rails_root/app/assets/webpack/landing.jpg

        let img = require('../../../assets/landing.jpg'); // I've tried requiring in different ways, and it's being found with this ugly ../../ .... format. Can verify since using an incorrect name throws an error.
        console.log('IMG is ' + img); // Returns a correct "landing.jpg" string

Here's what my webpack.config.js file looks like -

/* eslint comma-dangle: ["error",
  {"functions": "never", "arrays": "only-multiline", "objects":
"only-multiline"} ] */

const webpack   = require('webpack');
const path      = require('path');
const devBuild  = process.env.NODE_ENV !== 'production';
const nodeEnv   = devBuild ? 'development' : 'production';
const glob      = require('glob');
const isDebug   = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');

const config = {
  entry: [
    'es5-shim/es5-shim',
    'es5-shim/es5-sham',
    'babel-polyfill',
    './app/index.jsx',
  ].concat(glob.sync("./app/views/*/*/*", "./app/*/*/*")), // Allows you to use ReactOnRails.register({ }); in a components file rather than registering it in index.jsx

  output: {
    filename: 'webpack-bundle.js',
    path: '../app/assets/webpack',
    "publicPath": "/",
  },

  resolve: {
    extensions: ['', '.js', '.jsx'],
    alias: {
      react: path.resolve('./node_modules/react'),
      'react-dom': path.resolve('./node_modules/react-dom'),
      'ui_elements': path.resolve('./app/ui_elements'),
      'assets': path.resolve('./app/assets'),
      'vendor': path.resolve('./vendor'),
    },
  },

  plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jquery: "jQuery",
      "window.jQuery": "jquery"
    }),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(nodeEnv),
      },
    }),
    new webpack.ProvidePlugin({
      "React": "react",
    }),
  ],

  module: {
    loaders: [
      {
        test: require.resolve('react'),
        loader: 'imports?shim=es5-shim/es5-shim&sham=es5-shim/es5-sham',
      },
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
        loader: 'file-loader',
        query: {
          name: 'assets/[name].[ext]?[hash:8]',
        },
      },
      {
        test: /\.(mp4|webm|wav|mp3|m4a|aac|oga|png|gif)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          name: 'assets/[name].[ext]?[hash:8]',
          limit: 10000,
        },
      },
    ],
  },

};

module.exports = config;

if (devBuild) {
  console.log('Webpack dev build for Rails'); // eslint-disable-line no-console
  module.exports.devtool = 'eval-source-map';
} 
else {
  config.plugins.push(new webpack.optimize.DedupePlugin());
  console.log('Webpack production build for Rails'); // eslint-disable-line no-console
}

I can't remember exactly what fixed it but I'm pretty sure its going to assume your base route is rails_root/app/ and not in relation to the rails_root/client folder. If you don't see the image in your rails_root/app/assets/images/ folder then you need to figure out why that is first.

Thanks @ryangrush. Looks like I only needed to add "publicPath": "/", I got the suggestion from this SO:

screen shot 2017-01-24 at 15 53 39

It's all working now!

@estebanbouza glad you were able to figure it out!

Maybe we should put this information in the /docs directory?

Also nice to include it in sample application. Since if you encapsulate styles in component, you want and need encapsulate images too. Otherwise you wouldn't get truly isolated package which you can reuse in several projects.

I've been trying to get this solution to work for days now. My images are client/app/bundles/assets/images, and Webpack seems to be generating valid URLs. The problem I'm running into is that although react_on_rails is apparently generating symlinks to the files, I can't prove it, nor can I see if Rails is publishing my images in the asset pipeline. Is there any way to get Rails to show me all the fingerprinted files, or even better, all the files being served, so I can ensure that the symlinks are where I think they should be.

@1simonfish You should test this out locally, by doing things like:

  1. Precompiling with RAILS_ENV=production
  2. running rails in production mode, etc.

You can inspect the deployment files in /public/assets

This is all from memory...Actual details may differ.

Please get in touch with me if you want personalized help.

Hey @justin808 are you still looking for a doc PR for this? If so, and @ryangrush doesn't want dibs on this, I have a doc I wrote up about this that I could add.

That would be awesome. @tmobaird.

FWIW, I've typically used images in the Rails install as background images in the CSS. However, it's perfectly reasonable to use an image tag.

@justin808 that's definitely a good thing to note! I'll open a PR in a little bit with the doc for this

I am trying to precompile images to the public/assets directory but to no avail. Webpack works as intended and all my assets are copied to app/assets/webpack/webpack-assets folder.
The option on the initializers/react_on_rails config.symlink_non_digested_assets_regex is the default value. No symlinks are created for the images as i can see from the assets:precompile log and also using:

RAILS_ENV=production rake assets:clobber 

and then

RAILS_ENV=production rake assets:precompile

public/assets do not include the images too.
Any reason why this is happening and how i can debug it?

@justin808 i managed to resolve the issue in the end.
I am using Rails 5.1.0.rc1.
My configuration inside initializers/assets.rb file is the same as with dummy.
The thing that i was missing is that i didn't include inside the Sprocket's manifest.js file under app/assets/config the directory where my webpack assets reside like so

//= link_tree ../webpack/webpack-assets
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jtibbertsma picture jtibbertsma  路  7Comments

justin808 picture justin808  路  4Comments

evolve2k picture evolve2k  路  4Comments

chintanparikh picture chintanparikh  路  5Comments

hoenth picture hoenth  路  4Comments