You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Proposal: explicit named imports for non-JS/CSS assets #3722
After getting used to it, you’ll probably be comfortable with this giving you a URL.
But what about other types? For example, what should this return?
importdocfrom'./doc.md';
Markdown source? Compiled HTML? An AST?
What about this?
importIconfrom'./icon.svg';
Should this give you a link? The SVG content? A React component?
The usual answer is “decide it for yourself in the configuration file”. However, that doesn’t work for CRA so we decided to treat all unknown extensions as URL imports. This is not ideal because in some cases it just doesn’t make any sense, and in others there are advanced (but still relatively common) use cases that aren’t satisfied.
Proposal
What if we allowed to user to pick what they want, from a limited supported subset per filetype?
Named imports are checked by webpack so you’d get a compile error if you use an unsupported one.
Things that are unused will be tree shaken so if you only use e.g. HTML of Markdown files, their source won’t be bundled. Same for SVGs (whether you consume them as raw source, URLs, or React components).
Other zero-configuration tools can also adopt this approach.
Concerns
What do we do with the default import? Ideally I’d like to forbid it for anything other than JS/CSS because the intent is not clear for asset files (which version do you get?) We could do this with a lint rule.
If we make the breaking change, how do we update the consumers? We could write a codemod (it should be very simple).
It would be nice to coordinate this across at least a few other projects (e.g. @ndelangen Storybook). Maybe @KyleAMathews (Gatsby) @devongovett (Parcel) @rauchg (Next) would also be interested? I imagine we’ll need to write a multi-file Webpack loader for this, but I don’t see why other bundlers couldn’t adopt a similar convention.
Build performance: generating all possible content variations can be too slow. Ideally it would be nice if loaders had information about which import was used.
I think it is a very good thing to adopt a convention on it.
Even without talking about zero-configuration, today we can't use several Webpack loaders safely. We had the problem with svgr + url-loader. I made a workaround to solve it but it should not be a workaround.
I think we should add @sokra in this discussion, Webpack could force it by design.
What do we do with the default import? Ideally I’d like to forbid it for anything other than JS/CSS because the intent is not clear for asset files (which version do you get?)
We may want to define the rules for the default as and extension of "pick what they want, from a limited supported subset per filetype".
I think we should add @sokra in this discussion, Webpack could force it by design.
I think this needs to be a grassroots effort because enforcing anything like this from webpack’s side is going to take a super long time, even if they do it.
My thinking is that your example shouldn't build because it has a default import (import Icon). I think that if we adopt this approach we should just forbid default import and ask people to be explicit about what they want.
We could maybe keep default import for compatibility reasons for a while.
If you want to foster a culture of zero config tooling, then it would be nice to come up with a convention that doesn't break module imports by default. Using the import statement for anything non-module is already forcing a specific tool chain dependency and thus a default config requirement on the developer.
I'd really love to see a syntax that was actually JS compatible by not overloading the import statement
@Munter I understand this sentiment but it’s not very practical in my experience. We’re trying to do the best with what we got. FWIW, my proposal is actually closer to no lock-in than syntax like raw!./file.md because at least it is possible to auto-generate file.md.js that contains those exports. So in practice it can even work on Node if you have a codegen step.
Let’s not derail this thread with a general discussion whether import for assets is a good idea. The fact is that it solves many real use cases, and people want to do it. So the question here is how to make that more ergonomic and less confusing.
Reacted by Sunil Pai, Dmitrii 'Mamut' Dimandt, fellipe, Greg Bergé, Marcel Miranda Ackerman, Ran Yitzhaki, Ondřej Brejla, Michaël De Boey, Sean Roberts, Ryan Graham and 9 more
I think in order to adopt this we’ll have to keep supporting default import for a while. This will make it possible for tools like Storybook to catch up. Then we can decide whether we want to deprecate support for the default import or not.
I think I agree with you in principle, but I think there are potential downsides. Though as I'm writing them down, I feel like I might be wrong about some of my assumptions.
import * as Icon from 'icon.svg'
In the absence of a default export devs will still attempt the above, which will lead to multiple questions similar to "Why import * as React from 'react' and not import React from 'react'".
Why would dev need * from an svg? Because sometimes they just do :)
assumption that a default export is just an object with exposed/exported properties
Even though this assumption is incorrect due to how import/loading works, many devs (including me :) ) will assume the following code to be equivalent:
import{prop}from'something';// many will assume it to be equivalent toimportObjfrom'something';constprop=Obj.prop
It doesn't help that a lot of transpiled code looks that way.
My first proposal would be to create a multi-markdown-loader that would export all the formats we want. and it could even have a default export. What this default would be exactly, I don't know.
I also see potential for this idea to take hold for other files then markdown.
Supporting the default imports would only be less performant (from bundle size, and compile time aspects) and as you said, a lint rule could do the job for the depreciation period.
About the build performance problem, the loader will process all the possibilities, usually with no need, That might increase the build time.
Ideally, we would like the loader to be triggered by a combination of the extension and the named export, for example:
svg + url => use url-loader
import{url}from'./logo.svg';
svg + ReactComponent => use react-svg-loader
import{ReactComponent}from'./logo.svg';
I understand that there is a problem to do it in webpack because we only get what the user imported after the loaders ran.
When using static import it should be possible to get that information at an earlier stage.
@sokra, is there a plan of doing something like this?
@viankakrisna it could, from the babel's perspective this is just a js syntac and it could transpile it to the custom loader format (import raw from 'raw!./file.md';)
File contents will be bundled within main.[hash].chunk.js.
Reacted by Andy Fleming, Joshua, Arnaud Barré, Moritz Stückler, Mike Edmunds, Ngoc Dao, Tom Clark, Natalie Rose, Dan Delany, Mohammed Faragallah and 33 more
Reacted by Andy Fleming, Sam Richard, Natalie Rose, jeshio, Dan Delany, duan, Paulo Martins, Magnus Maynard, Z, Jason Thorpe and 5 more
Reacted by Andy Fleming, Natalie Rose, Travis Fischer, Enzo Ferey, Paulo Martins, Z, Kostia P, Jing Jiawei, Bohdan Yarosh, Elias Lousseief and 5 more
Reacted by Jing Jiawei, Bohdan Yarosh and Richard T. Miles
@xAlien95 This would still require ejecting in CRA, right? It'd be great to find a way without ejecting.
@pReya, no, macros are there to not eject. The only thing a macro can't do is to handle react-hot-loading, so if you make an edit in a Markdown file imported with raw.macro you'll need to restart the developer server to see the changes.
Reacted by Moritz Stückler, Ngoc Dao and Cai Pengcheng
I think if I'm not going to be able to use a "regular" import, I'd rather npm install raw-loader and then import readme from '!raw-loader!./README.md'.
Reacted by dominictobias, Jason Thorpe, Phips Peter, Bohdan Yarosh, Michael Curry and rjgotten
Reacted by Adam Baker, Grant Burry, Roger Knapp, Teodor, Alan Pogrebinschi, Maksim Litvinov, Dmitry Zarva, Gavin Rehkemper, Roman, Bernd Wessels and 17 more
I think going with the solution @xAlien95 proposed and relying on babel-plugin-macros will be better than bloating CRA with more features like this proposal.
The only real actionable thing I'd suggest would be adding this to the official FAQ.
@wapiflapi I also expected hot reloads but this works differently. As far as I’m aware, that would need to be something webpack handles in order for hot-reloading to just work. But this isn’t webpack -- this is a Babel macro; Babel is actually reading and injecting your .md source as a string in your source code using Node.js functionality.
Unless I’m missing something, React apps can’t leverage Node functions like readFileSync, etc. because they’re web apps. The raw.macro import is very clever because it works around the CRA ecosystem, but in doing so also breaks hot-reloading.
You could watch the files for changes but you’d have to hard-start your React app every time, the equivalent of yarn start, which takes a few seconds.
This is at least my understanding of what’s going on. 🤔
raw.macro is broken and no longer works as per latest create-react-app version, upgrading the whole project to the latest react is a bit painful.
Using import !raw-loader!... doesn't seem to import the entire file as string but spits out the url reference, which I cannot use for .glsl shader files.
I did eventually export the whole files as a huge single ES6 templated string
As of Webpack 5 you don't need anything special anymore. The only thing you need is CRA to add a module rule that treats known query string values as monikers to map to either the asset/inline or asset/source asset module type and then you're done: https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax
At the very least CRA should just define the given example rule for ?raw as an escape hatch, and then leave it up to application developers to further handle that content how they want to. This is compatible with dynamic imports as well, so it handles both inlined source as well as on-demand content. If you need it on-demand; simply use a dynamic import.
I think the problem is the ambiguity in imports. In modern JavaScript (specifically React), you often import files that aren't code, like images or documentation. The solution for this is using the named imports
// Ambiguous: What is 'doc'?
import doc from './doc.md';
This is kind of off be it's trying to find what is doc
// Explicit: I want the HTML version of this markdown file
import { html as docHtml } from './doc.md';
// Explicit: I want the URL of this logo
import { url as logoUrl } from './logo.png';
// Explicit: I want this SVG as a React Component
import { ReactComponent as Icon } from './icon.svg';
This way it's more suitable because the text outlines several benefits and "thoughts" behind this.
Clarity: The code explicitly states how the asset will be used.
Tree Shaking: This is a performance term. If a Markdown file loader can generate both HTML and an AST, but you only import { html }, the build tool (Webpack) is smart enough to throw away the AST code. It won't end up in your final app bundle.
Compile-Time Safety: If you try to import { threeDModel } from a .txt file, Webpack can throw an error immediately because that export name isn't supported for that file type
Activity
gregberge commentedon Jan 9, 2018
I think it is a very good thing to adopt a convention on it.
Even without talking about zero-configuration, today we can't use several Webpack loaders safely. We had the problem with svgr + url-loader. I made a workaround to solve it but it should not be a workaround.
I think we should add @sokra in this discussion, Webpack could force it by design.
dmitriid commentedon Jan 9, 2018
We may want to define the rules for the default as and extension of "pick what they want, from a limited supported subset per filetype".
Will this work in practice though?
gaearon commentedon Jan 9, 2018
I think this needs to be a grassroots effort because enforcing anything like this from webpack’s side is going to take a super long time, even if they do it.
gaearon commentedon Jan 9, 2018
@dmitriid
My thinking is that your example shouldn't build because it has a default import (
import Icon). I think that if we adopt this approach we should just forbid default import and ask people to be explicit about what they want.We could maybe keep default import for compatibility reasons for a while.
Munter commentedon Jan 9, 2018
If you want to foster a culture of zero config tooling, then it would be nice to come up with a convention that doesn't break module imports by default. Using the
importstatement for anything non-module is already forcing a specific tool chain dependency and thus a default config requirement on the developer.I'd really love to see a syntax that was actually JS compatible by not overloading the
importstatementgaearon commentedon Jan 9, 2018
@Munter I understand this sentiment but it’s not very practical in my experience. We’re trying to do the best with what we got. FWIW, my proposal is actually closer to no lock-in than syntax like
raw!./file.mdbecause at least it is possible to auto-generatefile.md.jsthat contains those exports. So in practice it can even work on Node if you have a codegen step.Let’s not derail this thread with a general discussion whether
importfor assets is a good idea. The fact is that it solves many real use cases, and people want to do it. So the question here is how to make that more ergonomic and less confusing.gaearon commentedon Jan 9, 2018
I think in order to adopt this we’ll have to keep supporting default import for a while. This will make it possible for tools like Storybook to catch up. Then we can decide whether we want to deprecate support for the default import or not.
dmitriid commentedon Jan 9, 2018
@gaearon
I think I agree with you in principle, but I think there are potential downsides. Though as I'm writing them down, I feel like I might be wrong about some of my assumptions.
import * as Icon from 'icon.svg'In the absence of a default export devs will still attempt the above, which will lead to multiple questions similar to "Why
import * as React from 'react'and notimport React from 'react'".Why would dev need
*from ansvg? Because sometimes they just do :)Even though this assumption is incorrect due to how import/loading works, many devs (including me :) ) will assume the following code to be equivalent:
It doesn't help that a lot of transpiled code looks that way.
ndelangen commentedon Jan 9, 2018
My first proposal would be to create a
multi-markdown-loaderthat would export all the formats we want. and it could even have a default export. What this default would be exactly, I don't know.I also see potential for this idea to take hold for other files then markdown.
gaearon commentedon Jan 9, 2018
Note there’s also a build performance concern: https://mobile.twitter.com/wSokra/status/950713344163446785.
ranyitz commentedon Jan 9, 2018
@gaearon, That sounds like an awesome idea!
Supporting the default imports would only be less performant (from bundle size, and compile time aspects) and as you said, a lint rule could do the job for the depreciation period.
About the build performance problem, the loader will process all the possibilities, usually with no need, That might increase the build time.
Ideally, we would like the loader to be triggered by a combination of the extension and the named export, for example:
svg+url=> useurl-loadersvg+ReactComponent=> usereact-svg-loaderI understand that there is a problem to do it in webpack because we only get what the user imported after the loaders ran.
When using static import it should be possible to get that information at an earlier stage.
@sokra, is there a plan of doing something like this?
gaearon commentedon Jan 9, 2018
Unfortunately @sokra says it’s not easy due to the current architecture: https://mobile.twitter.com/wSokra/status/950728052442529793
viankakrisna commentedon Jan 9, 2018
maybe it can be implemented as a babel-plugin?
becomes
I think i've seen similar approach (rewriting imports) with babel-plugin-lodash. Can it be applied to loaders too?
Andarist commentedon Jan 9, 2018
@viankakrisna it could, from the babel's perspective this is just a js syntac and it could transpile it to the custom loader format (
import raw from 'raw!./file.md';)41 remaining items
dmwyatt commentedon Jan 23, 2019
Anyone still thinking about or working on this?
Is there currently a workaround with CRA to get file contents as a string?
edit: Meaning, other than using the
!raw-loader!syntax.xAlien95 commentedon Jan 28, 2019
@dmwyatt, you can use a babel-plugin-macro such as raw.macro.
File contents will be bundled within
main.[hash].chunk.js.pReya commentedon Feb 1, 2019
@xAlien95 This would still require ejecting in CRA, right? It'd be great to find a way without ejecting.
xAlien95 commentedon Feb 1, 2019
@pReya, no, macros are there to not eject. The only thing a macro can't do is to handle react-hot-loading, so if you make an edit in a Markdown file imported with
raw.macroyou'll need to restart the developer server to see the changes.dmwyatt commentedon Feb 1, 2019
I think if I'm not going to be able to use a "regular" import, I'd rather
npm install raw-loaderand thenimport readme from '!raw-loader!./README.md'.devuxer commentedon Feb 11, 2019
@gaearon, Is this still under consideration?
transitive-bullshit commentedon Aug 27, 2019
I think going with the solution @xAlien95 proposed and relying on
babel-plugin-macros will be better than bloating CRA with more features like this proposal.The only real actionable thing I'd suggest would be adding this to the official FAQ.
zaydek commentedon Feb 15, 2020
I just wanted to add that
raw.macrois working extremely well for me:OP here.
wapiflapi commentedon Apr 5, 2020
Using
raw.macro, how can I add those files to the list of filesnpm startwatches?When changing the contents of the markdown file I expect a reload. I can't seem to find how to achieve this without ejecting.
Ideally it would watch only the "imported" files, but if I could set it up so it watches
*.mdI'd be fine with that too.If any one can help me out, this might be a useful thing to add to a FAQ (or at least this issue which google digs up.)
zaydek commentedon Apr 5, 2020
@wapiflapi I also expected hot reloads but this works differently. As far as I’m aware, that would need to be something webpack handles in order for hot-reloading to just work. But this isn’t webpack -- this is a Babel macro; Babel is actually reading and injecting your
.mdsource as a string in your source code using Node.js functionality.Unless I’m missing something, React apps can’t leverage Node functions like
readFileSync, etc. because they’re web apps. Theraw.macroimport is very clever because it works around the CRA ecosystem, but in doing so also breaks hot-reloading.You could watch the files for changes but you’d have to hard-start your React app every time, the equivalent of
yarn start, which takes a few seconds.This is at least my understanding of what’s going on. 🤔
dan-dr commentedon May 20, 2020
Doesn't seem to work with dynamic imports
luigimannoni commentedon Apr 16, 2023
raw.macrois broken and no longer works as per latest create-react-app version, upgrading the whole project to the latest react is a bit painful.Using import
!raw-loader!...doesn't seem to import the entire file as string but spits out the url reference, which I cannot use for.glslshader files.I did eventually export the whole files as a huge single ES6 templated string
rjgotten commentedon Oct 30, 2023
As of Webpack 5 you don't need anything special anymore. The only thing you need is CRA to add a module rule that treats known query string values as monikers to map to either the
asset/inlineorasset/sourceasset module type and then you're done:https://webpack.js.org/guides/asset-modules/#replacing-inline-loader-syntax
At the very least CRA should just define the given example rule for
?rawas an escape hatch, and then leave it up to application developers to further handle that content how they want to. This is compatible with dynamic imports as well, so it handles both inlined source as well as on-demand content. If you need it on-demand; simply use a dynamic import.ravin00 commentedon Nov 24, 2025
I think the problem is the ambiguity in imports. In modern JavaScript (specifically React), you often import files that aren't code, like images or documentation. The solution for this is using the named imports
This is kind of off be it's trying to find what is doc
This way it's more suitable because the text outlines several benefits and "thoughts" behind this.
rjgotten commentedon Nov 25, 2025
No. The solution for this is import attributes