A package for automaticaly minifying CSS, SCSS and Javascript for Laravel

Maintainers

Package info

github.com/nickdekruijk/minify

pkg:composer/nickdekruijk/minify

Transparency log

Statistics

Installs: 13 233

Dependents: 0

Suggesters: 1

Stars: 5

Open Issues: 0

5.0.2 2026-08-14 07:48 UTC

This package is auto-updated.

Last update: 2026-08-14 07:48:50 UTC


README

A simple package to minify CSS/SCSS and Javascript on the fly without the need of tools like Laravel Mix or Webpack. It combines all stylesheet files or javascript files into a single, minified file with simple but effective cachebusting on the content hash of the build.

Version 2 is a completely new package (version 1 is archived here) using scssphp/scssphp and tedivm/jshrink. Because minify now replaces natxet/cssmin with scssphp it can now compile SASS/SCSS code too!

Installation

Begin by installing this package with composer.

composer require nickdekruijk/minify

Upgrading to 5.0

Existing calls, filenames and config keys keep working, and the cache busting token quietly becomes a content hash instead of a filemtime(). What makes this a major is that two things change on their own.

staging is now in the default skip_environment, so a staging server serves its build instead of compiling one. If yours relied on compiling live, run php artisan minify:build in the deploy, or publish the config and take staging back out. A deployed environment under another name (acceptance, uat, demo) was never in the list and still compiles live; add it while you are there. See Source maps for why this matters beyond the compiling.

And the stylesheet <link> no longer carries blocking="render". It never did anything, a stylesheet link blocks rendering by itself and the attribute is for resources that do not, and low in the body it forbade the browser from painting what stood above it. Only matters if you assert on that HTML.

Views can use @minifyStylesheet and @minifyJavascript instead of {!! Minify::… !!} now. The facade is not going anywhere; see Stylesheet below.

Two things are worth knowing. Each build now has a .manifest.json next to it, recording every file the build was compiled from. It holds paths relative to the project and no file contents.

Committing it along with the build is worth doing but not required. An environment that compiles writes its own manifest on the first request. An environment that only serves — anything in skip_environment — falls back to the filemtime() token when there is no manifest, exactly as 4.0 did, and nothing breaks. What you give up is a stable token: git does not preserve mtimes, so every deploy hands the build a new one and browsers re-download CSS that never changed. With the manifest the token only moves when the content does.

And source maps are on wherever minify compiles, which means a build compiled while developing carries a sourceMappingURL comment. If you commit builds and deploy them, run php artisan minify:build with your production environment before deploying: it writes neither a map nor the comment. Otherwise commit the .map too, and know that it contains your complete Sass source.

Building for production

skip_environment holds production and staging: the environments that are deployed and reachable from the outside. If yours goes by another name, acceptance, accept, uat, demo, publish the config and add it. Compiling on the fly costs a stat of every source on every request, and it writes a source map that holds your complete Sass.

That leaves you deploying a build somebody remembered to commit. Configure your builds instead and let the deploy compile them:

// config/minify.php
'builds' => [
    ['stylesheet' => ['reset.css', 'app.scss']],
    ['javascript' => ['app.js']],
],
php artisan minify:build

It ignores skip_environment, mirrors the file lists you pass in your views, and takes an optional 'output' per build. --force compiles even when nothing changed; --clean removes the build, its manifest and its map first.

Changing output afterwards leaves the previous build where it was — minify keeps no record of paths it used before, so remove those by hand.

Debugging in the browser

Compressed CSS is one long line, so without a source map the inspector can only point at app.css:1. Minify writes a .map next to the build wherever it compiles, and none in a skip_environment. The sourcemap config key forces it on or off.

The map embeds your Sass sources, because resources/ is not reachable over the web and the browser could not fetch them otherwise. A published map is therefore your complete Sass source, which is why it is off where builds are only served.

Which makes skip_environment the setting that decides this. Any environment outside that list compiles, and any environment that compiles and is reachable from the outside serves its own Sass. production and staging are in it; add whatever else of yours is deployed.

Javascript has no equivalent: JShrink cannot produce source maps. Setting jsMinify to null leaves the bundle unminified wherever minify compiles, which at least keeps it readable. It defaults to true, because a build compiled while developing is often the one that ends up deployed.

Suggested .gitignore:

/public/css/builds/*.map
/public/js/builds/*.map

Sass support

Stylesheets are compiled by scssphp, which implements @import but not Sass modules — @use and @forward raise Sass modules are not implemented yet.

Entry files are @imported rather than pasted together, so each keeps its own name and line numbers in the source map. Variables still carry over from one entry file to the next, the way concatenating them did. Files that are not Sass are pasted in at their own position, because Sass turns @import of a .css file into a CSS @import rule instead of inlining it.

Upgrading from 3.x to 4.0

Two defaults changed. If you never published config/minify.php, both apply to you.

Import paths are now absolute. They used to be relative ('../resources/sass/'), which only resolves when the working directory is public/ — true for a web request, false for anything run through artisan. So a compile from the console looked for your stylesheets one directory above the project and threw not found within importPaths. The defaults now use resource_path() and public_path(), which resolve the same files from any working directory. A published config keeps whatever is in it, so if yours still has relative paths and you run minify from the console, make them absolute the same way.

testing is no longer skipped. With it in skip_environment, minify did not compile during tests at all: it pointed at the output file and hoped it was there. That made a test suite depend on a build left behind by an earlier browser request — green locally, and either absent or stale on a fresh CI checkout. Tests now compile from your sources. To keep the old behaviour, publish the config and add 'testing' back to skip_environment.

The default arguments of Minify::stylesheet() and Minify::javascript() dropped their relative prefix, from ['../resources/sass/app.scss'] to ['app.scss'] and from ['../resources/js/app.js'] to ['app.js']. Both resolve through the import paths, so this works whether your config is relative or absolute. Calls that pass files explicitly are unaffected.

Upgrading from 1.x

When upgrading change your projects composer.json to require nickdekruijk/minify with at least version "^2.0" and run composer update.

If you use .gitignore to ignore the old builds in js/builds and css/builds dont' forget to remove them from your .gitignore file and delete all obsolete build .css and .js files.

You may also need to change the Minify::stylesheet and Minify::javascript calls in your code/views since pathname might change depending on your configuration.

Laravel installation

Publish the config file if the defaults doesn't suite your needs:

php artisan vendor:publish --provider="NickDeKruijk\Minify\ServiceProvider" --tag=config

Stylesheet

{{-- app/views/hello.blade.php --}}
<html>
    <head>
        ...
        @minifyStylesheet(['lightbox.css', 'fonts.css', 'styles.css'])
    </head>
    ...
</html>

Javascript

{{-- app/views/hello.blade.php --}}
<html>
    <body>
        ...
        @minifyJavascript(['lazyload.min.js', 'scripts.js'])
        {{-- Or: --}}
        @minifyJavascript(['https://cdn.jsdelivr.net/npm/vanilla-lazyload@12.4.0/dist/lazyload.min.js', 'scripts.js'])
    </body>
</html>

The facade is still there and does the same thing, for a controller or anything else that is not a view:

$tag = Minify::stylesheet(['lightbox.css', 'fonts.css', 'styles.css']);

Attributes

Both take an array of extra attributes for the tag. true writes a boolean attribute on its own, false and null leave it out, so a caller can decide with an expression instead of building the array conditionally.

@minifyJavascript(['scripts.js'], null, ['defer' => true])
@minifyStylesheet(['print.scss'], null, ['media' => 'print'])

A script at the foot of the body still blocks the parser where it stands, and the parser has to finish before anything paints, so defer is usually what you want.

Inline

@minifyInlineStylesheet and @minifyInlineJavascript write the compiled build into the page instead of linking to it:

@minifyInlineStylesheet(['reset.css', 'app.scss'])

A link costs a request on a connection that has only just finished the first, and nothing can paint until it lands. Inline it arrives with the HTML that needs it. The price is that the bundle rides along on every page and the browser can no longer cache it across them, so this is for sites that mostly get first visits, or for a small critical bundle beside a linked one.

The build is compiled and cached exactly as it is for a link, so the two are interchangeable. There is no cache busting token, because there is no request to bust.

An inlined stylesheet points at its source map by url rather than by filename: written into the page there is no file for a bare app.css.map to resolve against, so the browser would look for it next to the document.

Config

See the config file at /config/minify.php