webpacker: stylesheet_pack_tag not working in webpacker 4??

based on the usage mention here, you can link them by using stylesheet_pack_tag which mean you add <%= stylesheet_pack_tag 'application' %> under views>layout>application.html.erb

but then when I view-source the web, there is no css loaded inside the <head>.

then to fixed this issue, i need to import import '../src/application.css' into packs/application.js to make the css file to work and load.

my question, is this how it should work? if so, what is the purpose to have <%= stylesheet_pack_tag 'application' %>? if you commented out this line, it is still work.

I have create the sample app for better picture about this

or maybe i miss some point somewhere else?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 25
  • Comments: 22 (11 by maintainers)

Commits related to this issue

Most upvoted comments

This sounds to me like misunderstanding how webpack works (which is reasonable, because it’s incredibly confusing).

Webpack is used for building many things, and the way we tell webpack what to load are import or require statements. This includes css files, images, and everything else. When you do import '../src/application.css', you’re telling webpack include application.css in the build. This does not mean it’s going to be compiled into your javascript, only that webpack now knows that you want to compile this file. How that file compilation is handled is depending on how your loaders (css-loader, sass-loader, file-loader, etc… ) are configured.

When you do <%= stylesheet_pack_tag 'application' %>, that’s a run-time inclusion from Rails, that has nothing to do with whether webpack has built the stylesheets or not. All that is doing is saying “if you have a pack named application-*.css, include it here”. If webpack didn’t build a separate pack of css, then this statement will have nothing to load because no stylesheets were compiled.

You didn’t mention what framework you’re using, and how this works is dependent on that. As far as I remember, stylesheet_pack_tag is only used for Vue because vue-loader automatically collects all of the stylesheets into one file. I am not sure if any other frameworks require this (I don’t think React does, for example).

It is perfectly normal and expected to import or require your stylesheets like you’re doing in your entry point file.

For what it’s worth, I’ve noticed that while stylesheet_pack_tag does not render anything in development, it does render a css file in production.

My guess is that this is due to the extract_css setting in config/webpacker.yml.

I saw that its covered in the document as

If you have hmr turned to true, then the stylesheet_pack_tag generates no output, as you will want to configure your styles to be inlined in your JavaScript for hot reloading. During production and testing, the stylesheet_pack_tag will create the appropriate HTML tags.

However, the problem I’ve observed with this is that if you have stylesheet_pack_tag in your layout as a default, but your javascript doesn’t spit out any styles, then you will run into an error on live where rails complains about stylesheet not existing.

This should be consistent for dev and production environment. As in, if error is to be generated on production for no stylesheet generated, then development should also give the error.

So what is the conclusion here?

I’m using webpacker with react and, by importing the scss in my packs/*.js, the styles work…in development. When I deployed to production, I realized there are no styles available.

Do I need to add stylesheet_pack_tag for it to work in production?

I think it would be better if I’d receive any error at any point.

I’ve also checked https://github.com/rails/webpacker/blob/master/docs/css.md and many other issues. Maybe this should be clarified more?

EDIT: I got it working in production by doing stylesheet_pack_tag 'index', the same as the js file that includes the scss file. Here’s the full solution:

// app/javascript/packs/index.js
import '../scss/index'; // this is index.scss file

// app/views/home/index.html.erb
<%= stylesheet_pack_tag 'index' %>
<%= javascript_pack_tag 'index' %>

I’m not sure if the scss file name matters. I think what matters is that stylesheet_pack_tag includes the same name like the js file name index.js.

  1. generally considered to be a bad practice in most instances

I would totally agree, but there are at least two cases we’ve encountered where reading the CSS outside of precompilation is necessary, and they both involve feeding CSS into a separate “packager”. It’s also a bit frustrating as it was, in a sense, within the scope of Sprockets, from which we’ve had to move away because of waning community support.

  1. We use wicked_pdf to generate PDFs from HTML documents, and having the CSS be external to the fed-in HTML creates issues. wicked_pdf bundles helpers that do effectively what you’re describing in item (2) or queries the webpack-dev-server if one’s available.

  2. We also need to feed CSS into premailer manually as it converts CSS into inline email-safe styling. To grab that CSS, much like wicked_pdf, we branch on the current environment and grab

For both items, this requires that we have the webpack-dev-server running during tests, which we haven’t been doing up to now, but might need to.

I think it would be nice to have a centralized way to grab CSS for a given manifest entry for cases like these. Do you think a feature request issue or PR would be welcome?

Anyway, thanks for your help @jakeNiemiec 🙂 Hope you are having a wonderful day!

From https://github.com/rails/webpacker/issues/2062#issuecomment-484942469

The build chain works like this:

(if extract_css == true) -> application stylesheet -> MiniCssExtractPlugin -> application.css -> stylesheet_pack_tag 'application'

(if extract_css == false) -> application stylesheet -> style-loader -> application.js (loads css in head)

MiniCssExtractPlugin does not support webpack-dev-server or HMR. statement from dev: https://github.com/webpack-contrib/mini-css-extract-plugin/issues/296#issuecomment-430679174

Good news time: a PR was just merged for this feature https://github.com/webpack-contrib/mini-css-extract-plugin/pull/334, but it won’t come to webpacker unless somebody creates a PR here.

It’s shoddy, but this gets me by for the time being:

module WebpackerHelper
  def inline_stylesheet_pack_tag(name)
    file_name = name + ".css"
    content_tag(:style) do
      if current_webpacker_instance.dev_server.running?
        open(inline_asset_url(file_name)).read.html_safe
      else
        File.read(File.join(Rails.root, "public", asset_pack_path(file_name))).html_safe
      end
    end
  end

  private

  def inline_asset_url(name)
    dev_protocol + current_webpacker_instance.config.dev_server[:public] + asset_pack_path(name)
  end

  def dev_protocol
    current_webpacker_instance.config.dev_server[:https] ? "https://" : "http://"
  end
end

It’d be really nice if there was a helper around this for the pdf/mailer use-cases.

When I deployed to production, I realized there are no styles available.

You probably have extract_css: true in your webpacker.yml. This removes import '../scss/index'; and generates the equivalent style sheet. You do need to add stylesheet_pack_tag yourself.

@eronisko when you are in development (depending on settings), the styles are being streamed in via WebSocket and injected into the <head>.

I tried open but it would complain about self signed cert invalid So I used net/http

require "net/http"
require "openssl"
require "uri"

def inline_stylesheet_pack_tag(name)
  file_name = name.end_with?(".css") ? name : name + ".css"
  tag.style do
    if current_webpacker_instance.dev_server.running?
      uri = URI(inline_asset_url(file_name))

      ::Net::HTTP.start(
        uri.hostname,
        uri.port,
        use_ssl: uri.scheme == "https",
        # Will be self signed cert
        verify_mode: OpenSSL::SSL::VERIFY_NONE,
      ) do |http|
        http.request_get(uri.path)
      end.body.force_encoding("UTF-8")
    else
      ::File.read(
        ::File.join(
          ::Rails.public_path,
          # Using `asset_pack_path` generates unnecessary info
          # like host and protocol
          current_webpacker_instance.manifest.lookup!(file_name),
        ),
      )
    end.html_safe
  end
end

Edit 1: Updated code for current_webpacker_instance.dev_server.running? == false after some testing It does skip the conditional inside asset_pack_path

Thanks @HarrisonB, today is much better.

Rendering emails and PDFs are definitely a webpack-dev-server use-case I hadn’t considered. There are serious hurdles that would need to be cleared before it could work since webpack-dev-server uses JS to juggle the styles. I am not sure how it could work in the context of PDFs & emails. You would probably need to run ./bin/webpack with extract_css: true to get what you need. (I’m more from the webpack side of the equation, can you tell? 🙂)

I think it would be nice to have a centralized way to grab CSS for a given manifest entry for cases like these. Do you think a feature request issue or PR would be welcome?

Most definitely! It is especially needed since rails@6 is out with webpacker as a default.

@jakeNiemiec Sorry, I was unclear. That would include a <link> tag in the <head> referencing a separate CSS file. However, if I were to want a <style> tag with the CSS as a child text node, I cannot achieve that via Webpacker (without client-side JS execution)?

So if I understand it correctly, there’s currently no way to have the CSS in the head without some client-side JS execution?

It is possible, just do the following and it should work in a default install:

  1. extract_css: true
  2. Put <%= stylesheet_pack_tag 'myStylePack' %> in <head>

Here is the mechanism:

https://github.com/rails/webpacker/blob/41d79c96187154b5485289e8c3c42428dd819bfc/package/utils/get_style_rule.js#L40-L44