Import a text file as a string without calling fetch() or filesystem APIs in
your application code. This is useful for HTML templates, shaders, SQL, GraphQL,
Markdown, and code examples.
The best syntax depends on your runtime or build tool. Import attributes are the emerging standard; Vite and older webpack setups have their own established solutions.
These imports are for files that are part of your module graph. Use fetch()
instead when the file is remote, changes independently of the build, or should
only be loaded in response to user input.
Using import attributes with { type: "text" }: the modern way
The modern syntax uses a
type: "text" import attribute:
import template from './template.html' with { type: 'text' }
console.log(typeof template) // "string"The file is decoded as UTF-8 and exposed as the module’s default export. Its
extension does not matter, so the same syntax works for .txt, .html, .sql,
or even .js files that must not be executed.
Dynamic imports use an options object and return a module namespace:
const { default: template } = await import('./template.html', {
with: { type: 'text' },
})There is one compatibility caveat: import attributes are standard JavaScript,
but the "text" type is still a Stage 3 proposal as of this revision. It is
already supported by runtimes and tools including Deno, Bun, and webpack. Node
supports it behind the --experimental-import-text flag. Browser support is
still rolling out, so check your targets before shipping it without a build
step.
Prefer this form when your runtime or bundler supports it. Unlike conventions
such as ?raw, it expresses the intent in standard JavaScript syntax.
Using Vite
Vite imports a file as a string when its specifier ends in
?raw:
import template from './template.html?raw'
import shader from './shader.glsl?raw'No Vite configuration is required. The file becomes part of the dependency graph, so editing it updates the development server and includes the new value in the next build.
?raw is a Vite convention, not JavaScript syntax. It is a good default for a
Vite application today, especially when the generated code must run in browsers
that do not yet support text import attributes.
Using webpack
Current webpack supports the standard import-attribute form without additional configuration:
import template from './template.html' with { type: 'text' }Webpack also provides the
asset/source module type. Use
it when you want imports with particular extensions to return strings:
// webpack.config.js
export default {
module: {
rules: [
{
test: /\.(html|md|sql|txt)$/,
type: 'asset/source',
},
],
},
}The matching files can then be imported normally:
import template from './template.html'For opt-in behavior similar to Vite’s, match a resource query instead:
// webpack.config.js
export default {
module: {
rules: [
{
resourceQuery: /raw/,
type: 'asset/source',
},
],
},
}import source from './Component.jsx?raw'The word raw has no special meaning to webpack here; it is simply the query
matched by the rule. If another rule processes the same file, exclude raw
requests from that rule so the source is not transformed first:
{
test: /\.[cm]?[jt]sx?$/,
resourceQuery: { not: [/raw/] },
use: 'babel-loader',
}This built-in asset module replaces the old raw-loader package and its inline
raw-loader!./file syntax.
Gatsby, for posterity
Gatsby Cloud reached end of life. Gatsby JS itself was not formally deprecated, but it moved toward maintenance and stability rather than major framework innovation. Existing Gatsby sites still need occasional webpack configuration, so here is the historical setup.
First try the standard import attribute if the webpack version in your Gatsby project supports it:
import source from './Component.jsx' with { type: 'text' }For older Gatsby and MDX builds, add an asset/source rule in gatsby-node.js.
The important part is excluding ?raw requests from the JavaScript rule added
by the MDX plugin. Otherwise Babel and React Refresh may modify the source
before webpack turns it into a string.
// gatsby-node.js
/** Adds raw text imports to Gatsby's webpack configuration */
exports.onCreateWebpackConfig = ({ actions, getConfig }) => {
const config = getConfig()
config.module.rules = config.module.rules.map(rule => {
// This was the JavaScript rule added by the Gatsby MDX plugin
if (String(rule.test) !== String(/\.(js|mjs|jsx)$/)) return rule
return { ...rule, resourceQuery: { not: [/raw/] } }
})
config.module.rules.push({
resourceQuery: /raw/,
type: 'asset/source',
})
actions.replaceWebpackConfig(config)
}You can then import the untouched source:
import source from './Component.jsx?raw'
<pre>
<code>{source}</code>
</pre>The exact JavaScript rule can differ between Gatsby and MDX plugin versions.
Inspect getConfig().module.rules and adjust the match if your project does not
use /\.(js|mjs|jsx)$/.