Angular-cli: Can't use relative paths in url() in scss files

Created on 29 Oct 2018  Â·  41Comments  Â·  Source: angular/angular-cli

Bug Report or Feature Request (mark with an x)

- [ ] bug report -> please search issues before submitting
- [x] feature request

Command (mark with an x)

- [x] new
- [x] build
- [ ] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] xi18n
- [ ] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc

Versions


npm: 6.4.1
node: v10.7.0
ng cli: 6.2.6
macOS High Sierra

Repro steps

  1. Create new project

ng new ng-sass-test--style=scss

  1. Add following files:
  - src
    - sprite
      - _sprite.scss
      - sprite.svg

With the following content:

# _sprite.scss
.sprite-common {
    background-image: url('sprite.svg');
}

Add the following line to src/styles.scss.
@import '~src/sprite/sprite';
Add the same line to src/app/app.component.scss.

The log given by the failure

ERROR in ./src/styles.scss (./node_modules/raw-loader!./node_modules/postcss-loader/lib??embedded!./node_modules/sass-loader/lib/loader.js??ref--15-3!./src/styles.scss)
Module Error (from ./node_modules/postcss-loader/lib/index.js):
(Emitted value instead of an instance of Error) CssSyntaxError: /***/ng-sass-test/src/sprite/_sprite.scss:2:22: Can't resolve 'sprite.svg' in '/***/ng-sass-test/src'

  1 | .sprite-common {
> 2 |     background-image: url('sprite.svg');
    |                      ^
  3 | }

ERROR in ./src/app/app.component.scss
Module Error (from ./node_modules/postcss-loader/lib/index.js):
(Emitted value instead of an instance of Error) CssSyntaxError: /***/ng-sass-test/src/sprite/_sprite.scss:2:22: Can't resolve 'sprite.svg' in '/***/ng-sass-test/src/app'

  1 | .sprite-common {
> 2 |     background-image: url('sprite.svg');
    |                      ^
  3 | }

Desired functionality


I would like to be able to use the relative file path in the scss file. I know this is solved by using an absolute path for the image. But I'm using the package svg-sprite, I'm not an expert in the package, but it looks like it doesn't support generation of an absolute path. Maybe I'm missing something in the angular-cli setup, so I can use this way of path-handling.

Mention any other details that might be useful

devkibuild-angular low broken bufix

Most helpful comment

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

All 41 comments

Just had a quick look at this, at this is tricky one.

Since in ./src/app/app.component.scss if you want to use relative url, the image src, should be ../sprite/sprite.svg, while forstyle.scss it should be ./sprite/sprite.svg.

Well yes, if I use a relative path in the one sass-file, it uses it as relative from every other file where I import it. Isn't there a way to just have it relative to the single file with the url()?

I think the recommended approach here is to put these sprite file in the assets folder, and use root level paths (starting with /) for urls in sass files that are imported by other sass files.

In scss files at an absolute path;

background-image: url('/sprite/sprite.svg');

in your angular.json add the following so that the sprite is copied, under architect -> build -> options

"assets": [
    "src/sprite/sprite.svg"
],

From svg-sprite package point of view, there will be no changes.

The problem with that is that I can't change the sprite.scss, it is generated by the svg-sprite package. For as far as I know, it does not have the option to generate with an absolute path.

The simplest way to do it is to copy https://github.com/jkphl/svg-sprite/blob/6966592489dfcf2cd9ed2bba500cb8ee31c00b9c/tmpl/css/sprite.scss and add a lead slashes / and when using svg-sprite you provide this template via configuration example

config            = {
    mode: {
        css: { // Create a «css» sprite
            render: {
                scss : {
                    template: 'my-custom.scss',
                }
            },
        },
    },
},

Tried it out, works perfect. Thanks for the idea @alan-agius4 !

Doesn't work same issue with absolute paths (at least behaviour is inconsistent with angular6 ). Happened after upgrade to 7. I think what changed is automatically adding resolved scss assets to assets option/public folder. Hence people reporting having to add each file to assets (At least in the past we didn't need to).

btw I am not getting any errors for entirely wrong paths either as I used to.
seeing this: https://github.com/angular/angular-cli/issues/12746 makes me think that updating angular +sass is a nightmare are there any instructions for updating? could this be caused by upstream projects as well?

is sass loader what angular uses internally?
https://github.com/webpack-contrib/sass-loader#problems-with-url

any update on this issue ?

I’m also interested on this, since Stylus and LESS work fine with relative URLs and SASS doesn’t. For ejected projects resolve-url-loader does the trick.

I get the following, rocking Angular CLI: 7.3.0, Angular: 7.2.3.

// angular.json
"assets": [
    "src/assets"
]
background-image: url('/src/assets/images/icon.png');

// GET http://localhost:3000/src/assets/images/icon.png 404 (Not Found)
background-image: url('../../../../assets/images/icon.png');

// GET http://localhost:3000/icon.png 200 OK

As @agalazis, correctly pointed out the issue. Sass itself doesn't provide Url rewiring. While indeed resolve-url-loader might work in some cases, this relies on a non standard behaviour as it uses sourcemaps to rewire the paths.

@ruimarques, I suggest you open a separate issue with a reproduction for the issue you are encountering.

@alan-agius4 another point to mention in the past svgs in my css but after giving up and setting assets path now they are loaded as links after build

Please consider not removing rebaseRootRelativeCssUrls build option (https://github.com/angular/angular-cli/issues/14587) until this issue is fixed. Since this issue prevents using relative URLs, the rebaseRootRelativeCssUrls option is the only workaround when needing to specify a base-href when building.

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

@iwnow This has been said to cause the app to be deployed with images copies to the dist folder (out of the assets folder) meaning you have multiple copies for no reason.

#

Angular Team:
Please fix this, it's very sad that it's now "The recommended method to transition is to use relative paths within the source stylesheet".

This ticket has been here for well over a year. What was the plan? Was there one? It seems the idea was to just break AngularCLI and say use this method that doesn't work and hope for the best.

@michael-letcher yes I understand but I have no multiple copies, all works as expected
this is a simple workaround, but I still want this to be fixed

background-image: url('../../assets/img/bg-masthead.jpg'); worked for me.
Very annoying.

Hi guys, I've had the same problem and I fixed it as @Dada-Tech has mentioned

I have my site on https://url/account, some background-image: url('/image.png') and it was working on Angular v7.0.2. However, it is not working after migrating to v8.2.1

v7.0.2
scss: background-image: url('/image.png')
url after deploy: https://url/account/image.png

v8.2.1
scss: background-image: url('/image.png')
url after deploy: https://url/image.png

Wouldn't setting the --base-href value during ng build solve your issue @lfdantoni? (see: https://github.com/angular/angular-cli/wiki/build#base-tag-handling-in-indexhtml)

The solution is to write the path as if you were starting from "angular.json".

Instead of
background-image: url('../assets/images/pattern-dot-grid.svg');

Write it like this (witout the dots at the beggining)
background-image: url('/assets/images/pattern-dot-grid.svg');

-- this worked for me ☺

A couple of the workarounds here are basically: "just use absolute paths". The issue is about the inability to use relative paths :D

In some cases you can't use absolute because you have a baseHrefUrl like /en/ so / is going out of that folder to a path that doesn't exist.

You can workaround this using Express I guess but

I am having a similar issue. My production server is running the solution in a subfolder (mydomain.com/myapp/). I have setup the base href to "/myapp/" on localhost it is set to "/".

I have a css file placed in "assets/css" this file is referring to a file in "assets/images", I have done that writing "/assets/images/myimage.png" This is working fine localhost, but when publishing to the server it can't find the image. If I change url on the server in the css to "assets/images/myimage.png", it works on the server.

Then I have tried changing the url in the project to the one on the server ("assets/images/myimage.png"), but then it can't compile I am getting the error: Can't resolve 'assets/images/myimage.png".

I have tried the following combinations, but without any luck :-|

'./assets/images/myimage.png'
'~assets/images/myimage.png'
'../assets/images/myimage.png'

@pnrdk did you try the following (note the / between the ~ and 'assets')?
~/assets/images/myimage.png

@pnrdk did you try the following (note the / between the ~ and 'assets')?
~/assets/images/myimage.png

This worked for me with the latest version of the CLI.

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

thankk you

You can use inline CSS style
<div style="background-image: url('/assets/images/logo.svg');" ></div>

@iwnow MVP, that really works! Works in latest Angular version (8.3.21)! 🚀

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

It's compiled to

background-image: url('/./assets/images/logo.svg');

in my case which is not what's expected.

Globally I'd recommand to never use absolute paths in url() as it is pure CSS and is always relative to the index.html file in production.

However, the compiler complains when using relative paths, especially if you import a variables.scss file containing assets in your components.

background-image: url('~/assets/images/logo.svg');

works for me (version 8.0.1)

the problem this works in dev env, but not when building the app. (in SCSS - didn't try plain CSS)

the build output for '~/assets/images/logo.svg' is -> '/assets/images/logo.svg' which prevents from deploying the app in any other path than the root, even though in my index.html i set the baseHref to be relative './'

There is still this fix if you can use custom-webpack.
It passes the webpack compilation and provides good relative paths in prod.

the only way i found how to fix it in builds is to specify the base-href attribute to be the same as the path i plan to deploy to, for example:

ng build --base-href ui
deploy the app under www.example.com/ui/

and then in the SCSS file use: background-image: url('~/assets/icon.svg').

after building the app, the ~ char is replaced with whatever value you provide to the --base-href attribute.

@bnohad unfortunately this didn't work with --localize flag that has been introduced in angular 9 to compile several language locales using one command.
I use ng build --prod --localize command and has configured 2 languages but there no added language directory to url path when I write like this background-image: url('~/assets/icon.svg')
But I see language directory in Html base tag. Probably this is bug of angular 9

The CLI also adjusts the HTML base HREF for each version of the application by adding the locale to the configured baseHref. https://angular.io/guide/i18n

Just found that when using a caret ^ as the first character on the url provided in url(), the angular building process ignores it (it's actually a postcss-loader plugin), so with that you can use anything including relative urls expecting different base-hrefs. example:

scss .box { background: url("^myfiles/images") }

Of course, there is an assets folder in angular where you will put the images (to have it working locally you can just url("^assets/images")) but the point is that when you want to deploy it in production, maybe you will copy your assets folder content to your server.com/myapp/myfiles folder, so in this case it's very useful, otherwise, the builder will try to resolve the path and it will throw an error because it can't found the files (actually, it has a problem when the url is relative),

In addition, as off-topic, when resolving the urls (not using ^), the files will be copied to the dist/project folder, not to dist/project/assets (that's the destiny of the assets folder configured in angular.json), so it will be quite a mess. A solution is to override the webpack scss loaders or (a clean way) is to use the next approach by including the target files in the assets directories, even of third party modules.

```json
"assets": [
"src/favicon.ico",
"src/assets",
{ "glob": "*/", "input": "./node_modules/mymodule/assets/", "output": "./assets/" }
],
````

For reference:
https://github.com/angular/angular-cli/blame/523a20d92bc516dbdf2ea17bc9b02d9402232c47/packages/angular_devkit/build_angular/src/angular-cli-files/plugins/postcss-cli-resources.ts#L72

Just found that when using a caret ^ as the first character on the url provided in url(), the angular building process ignores it (it's actually a postcss-loader plugin), so with that you can use anything including relative urls expecting different base-hrefs. example:

.box {
  background: url("^myfiles/images")
}

FYI - the caret sign was previously discussed in the following issue https://github.com/angular/angular/issues/32811

Thanks @dannyskoog , not supported feature means that maybe this hidden gem could be removed without warning?

Yes @devniel. And that was why I wasn't allowed to add it to the documentation (reference: https://github.com/angular/angular/pull/32921#discussion_r331118890)

Thanks It worked :+1:

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ericel picture ericel  Â·  3Comments

IngvarKofoed picture IngvarKofoed  Â·  3Comments

MateenKadwaikar picture MateenKadwaikar  Â·  3Comments

rajjejosefsson picture rajjejosefsson  Â·  3Comments

jmurphzyo picture jmurphzyo  Â·  3Comments