[Django]-What is the correct way to load CSS staticfiles using npm and Django?

9👍

What I do is the following,

STATICFILES_DIRS = (
    os.path.join(DJANGO_PROJECT_DIR, 'static'),
    os.path.join(ROOT_DIR, 'node_modules', 'd3', 'build'),
    os.path.join(ROOT_DIR, 'node_modules', 'c3'),
)

This will place only the needed files in my static folder.

7👍

To avoid importing many unused files in the static folder, I copy the items I need with a Gulp script. I use an NPM script to invoke Gulp after installing the packages, so it doesn’t affect my daily workflow.

package.json:

{
  "dependencies": {
    "bootstrap": "^4.3.1",
    "gulp": "^4.0.2",
    "jquery": "^3.4.1"
  },
  "scripts": {
    "install": "gulp"
  }
}

gulpfile.js

const { series, src, dest } = require('gulp');

// Task 1: copy bootstap's assets to /_vendor/
function bootstrap() {
  const files = [
    'node_modules/bootstrap/dist/css/bootstrap.min.css',
    'node_modules/bootstrap/dist/js/bootstrap.min.js'
  ]
  return src(files).pipe(dest('_vendor'))
}

// Task 2: copy jquery's assets to /_vendor/
function jquery() {
  const files = [
    'node_modules/jquery/dist/jquery.min.js'
  ]
  return src(files).pipe(dest('_vendor'))
}

exports.default = series(bootstrap, jquery)

my_project/settings.py:

STATICFILES_DIRS = [
    str(BASE_DIR / '_vendor'),  # populated by gulp
]

Django template:

{% load staticfiles %}
<link rel="stylesheet" href="{% static 'bootstrap.min.css' %}" />

Bonus 🎉

In the Gulpfile, you can concatenate and minify files. As an example, here is my Gulpfile for Blueimp’s file upload library:

const uglify = require('gulp-uglify');
const concat = require('gulp-concat');

// Task 3: minify blueimp's assets and save to /_vendor/
function blueimp() {
  const files = [
    'node_modules/blueimp-file-upload/js/vendor/jquery.ui.widget.js',
    'node_modules/blueimp-file-upload/js/jquery.iframe-transport.js',
    'node_modules/blueimp-file-upload/js/jquery.fileupload.js'
  ]
  return src(files).pipe(uglify())
                   .pipe(concat('jquery.fileupload.min.js'))
                   .pipe(dest('_vendor/'))
}

3👍

I personally think it’s fine to have these extraneous dev files living in node_modules (and duplicated into STATIC_ROOT). Your mileage may vary if your node_modules folder is gigantic, but doubling the size isn’t usually a big deal for my use cases, and it’s convenient to not have to list each module in STATICFILES_DIRS as I install them.

STATICFILES_DIRS = (
    ...
    os.path.join(BASE_DIR, 'node_modules'),
    ...
)

Then in your templates, do:

<script src='{% static 'bootstrap/dist/css/bootstrap.min.css' %}'></script>

If the folder ever did get out of hand size-wise, if you started encountering conflicts, or if there was a library you really didn’t want to serve, Jostcrow’s answer (or writing a custom staticfiles finder) could make sense.

Also, might be worth looking into trying to bundle everything together. If all of your stuff lives in bundled JS/CSS files, the whole issue is resolved by just webpack-ing those files into your app of choice.

0👍

I changed the way I do that since my previous answer. Now, I use django-npm, a ridiculously simple package that allows cherry-picking the files you want to import from node_modules/.
As a comparison, here is how I could achieve the same result as in my previous response:

package.json

{
  "dependencies": {
    "bootstrap": "^4.3.1",
    "jquery": "^3.4.1"
  }
}

my_project/settings.py

STATICFILES_FINDERS = (
  ...
  'npm.finders.NpmFinder',  # see django-npm's installation instructions
)

NPM_STATIC_FILES_PREFIX = 'node_modules'
NPM_FILE_PATTERNS = {
    'bootstrap': [
        'dist/js/bootstrap.bundle.min.*',
    ],
    'jquery': [
        'dist/jquery.min.*',
    ],
}

What about concatenation and minification?

I use django-compressor and django-sass-processor.

Leave a comment