Fix – Broken Image Paths in CSS While Using Gulp

While bundling CSS through GULP the most common problem that turns up is – broken image and font urls. It happens due to how path references work in stylesheets. Paths to images and fonts in a stysheet are “relative” to the location of that stylesheet.

Let us take an example

.semi {
background: url(../images/dummyimage.gif);
}

So this is a class located on a stylesheet style.css.

Let us look at the directory tree here

root -
	- images
		- dummyimage.gif
	- css
		- style.css

As you can see, the background image displays correctly as the image path relative to the stylesheet is correct.

Now let us move around the files and directories a bit. The new directory structure is

root -
	- images
		- dummyimage.gif
		- otherimage.jpg
	- js
		- script.js
		- custom.js
		- more.js
	- css
		- style.css
		- custom.css
		- more.css
	- dist
		- css
			- bundle.css
		- js
			- bundle.js
	- gulpfile.js
	- package.json

Here bundle.css is the output stylesheet generated by gulp – located in “dist” directory. This stylesheet can be minified and cleaned, but one thing that does not change is the URLs to images and fonts. For example the class .semi will still have the following background url:

background: url(../images/dummyimage.gif);

This url is broken now as the path to that background image “relative” to the stylesheet ( bundle.css ) is no longer correct.

Now there are several solutions to this problem but the most effective one is using a plugin gulp-replace, as this gives you the flexibility to deal with any CSS path related problems in gulp builds.

To use gulp-replace install it with npm

npm install –save-dev gulp-replace

then use it in your gulefile.js

gulp.task('styles', function () {

   gulp.src([ // your source css files
        '...',
	'...'
    ])        
    .pipe(concat('all.css'))
    .pipe(replace(pattern, replacement))
    .pipe(gulp.dest('./dist/'))

})

.pipe(replace(pattern, replacement)) – this is where the magic happens!

To understand how it works let us go back to the directory structure

root -
	- images
		- dummyimage.gif
		- otherimage.jpg
	- js
		- script.js
		- custom.js
		- more.js
	- css
		- style.css
		- custom.css
		- more.css
	- dist
		- css
			- bundle.css
		- js
			- bundle.js
	- gulpfile.js
	- package.json

In the following gulp task i am bundling all css files to bundle.css

gulp.task('styles', function () {
gulp.src([ // your source css files
         './css/style.css',
     './css/custom.css',
     './css/more.css'
     ])        
     .pipe(concat('bundle.css'))
     .pipe(gulp.dest('./dist/css/'))
})

Now, assume that custom.css has the following class in it

.semi { background: url(../images/dummyimage.gif) }

After running gulp this path will be broken in bundle.css as explained above. Let us fix that.

A look at the directory structure tells us that the path of that image relative to bundle.css is

../../images/dummyimage.gif

So we need to replace all occurrences of ../images/ with ../../images/ for all image urls in bundle.css

Let us create a regex pattern to detect the image urls first

/(url()(..\/images\/\w+(.svg|.gif|.png|.jpg)))/gi

The above pattern will detect all image urls that look like

../images/dummyimage.gif
../images/anyimage.jpg
../images/otherimage.png

etc..

Here we just need to replace “../images” with “../../images”

So here is the replacement for the above regex pattern

$1../$2

$1 represents “url(“
$2 represents the rest of the url

By inserting “../” between $1 and $2 we are creating a url like

url(../../images/(rest of the url here)) which is the correct image url for images referenced from bundle.css

The modified gulp task now looks like

gulp.task('styles', function () {
gulp.src([ // your source css files
         './css/style.css',
     './css/custom.css',
     './css/more.css'
     ])        
     .pipe(concat('bundle.css'))
     .pipe(replace(/(url()(..\/images\/\w+(.svg|.gif|.png|.jpg)))/gi, '$1../$2')
     .pipe(gulp.dest('./dist/css/'))
})

Thats it! Run the task now and all your image paths in css should work fine now.

Note that you can add multiple replaces for different image directory paths as follows

.pipe(replace(pattern1, replace1))
.pipe(replace(pattern2, replace2))

and so on.

Did that work for you? Let me know in the comments below.