# Import text files as strings in JavaScript

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](https://github.com/tc39/proposal-import-text):

```js
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:

```js
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`](https://vite.dev/guide/assets.html#importing-asset-as-string):

```js
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:

```js
import template from './template.html' with { type: 'text' }
```

Webpack also provides the
[`asset/source` module type](https://webpack.js.org/guides/asset-modules/). Use
it when you want imports with particular extensions to return strings:

```js
// webpack.config.js
export default {
  module: {
    rules: [
      {
        test: /\.(html|md|sql|txt)$/,
        type: 'asset/source',
      },
    ],
  },
}
```

The matching files can then be imported normally:

```js
import template from './template.html'
```

For opt-in behavior similar to Vite’s, match a resource query instead:

```js
// webpack.config.js
export default {
  module: {
    rules: [
      {
        resourceQuery: /raw/,
        type: 'asset/source',
      },
    ],
  },
}
```

```js
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:

```js
{
  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](https://www.netlify.com/blog/gatsby-cloud-evolution/).
Gatsby JS itself was not formally deprecated, but it
[moved toward maintenance and stability](https://www.netlify.com/blog/developing-gatsby-enhancing-stability-and-reliability/)
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:

```js
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.

```js
// 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:

```mdx
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)$/`.
