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

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

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 36
  • Comments: 41 (2 by maintainers)

Commits related to this issue

Most upvoted comments

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

works for me (version 8.0.1)

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.

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

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 ☺

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.

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

@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.

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.

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

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

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 ‘./’

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

@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

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

@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

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

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

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.

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)

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")
}

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.

"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

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.

@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.

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

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.