# File Types (/runtime/file-types)

<!-- agent-signals: reading_time_min: 11 · est_tokens: 4429 · updated: 2026-09-23 -->
Related: [Watch Mode](/runtime/watch-mode.md), [Debugging](/runtime/debugger.md), [REPL](/runtime/repl.md), [bunfig.toml](/runtime/bunfig.md), [Module Resolution](/runtime/module-resolution.md), [JSX](/runtime/jsx.md)

The Bun bundler implements a set of default loaders. As a rule of thumb, the bundler and the runtime support the same set of file types.

`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.json5` `.toml` `.yaml` `.yml` `.xml` `.txt` `.text` `.md` `.markdown` `.wasm` `.node` `.html` `.sh`

Bun uses the file extension to pick the built-in *loader* that parses the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](/bundler/plugins) that extend Bun with custom loaders.

To specify a loader explicitly, use the `type` import attribute.

```ts
import my_toml from "./my_file" with { type: "toml" };
// or with dynamic imports
const { default: my_toml } = await import("./my_file", { with: { type: "toml" } });
```

<Note>
  With TypeScript 7.1 or newer, `@types/bun` types these imports from the `type` attribute: a `type: "text"` import is a
  `string`, a `type: "sqlite"` import is a `Database`. Older TypeScript versions type the import from the file extension
  alone.
</Note>

***

## Built-in loaders [#built-in-loaders]

### `js` [#js]

**JavaScript**. Default for `.cjs` and `.mjs`.

Parses the code and applies a set of default transforms like dead-code elimination and tree shaking. Bun does not down-convert syntax.

### `jsx` [#jsx]

**JavaScript + JSX**. Default for `.js` and `.jsx`.

Same as the `js` loader, but JSX syntax is supported. By default, Bun down-converts JSX to plain JavaScript; the details depend on the `jsx*` compiler options in your `tsconfig.json`. Refer to the TypeScript documentation [on JSX](https://www.typescriptlang.org/docs/handbook/jsx.html).

### `ts` [#ts]

**TypeScript loader**. Default for `.ts`, `.mts`, and `.cts`.

Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking.

### `tsx` [#tsx]

**TypeScript + JSX loader**. Default for `.tsx`. Transpiles both TypeScript and JSX to vanilla JavaScript.

### `json` [#json]

**JSON loader**. Default for `.json`.

JSON files can be directly imported.

```ts
import pkg from "./package.json";
pkg.name; // => "my-package"
```

During bundling, Bun inlines the parsed JSON into the bundle as a JavaScript object.

```ts
var pkg = {
  name: "my-package",
  // ... other fields
};
pkg.name;
```

If you pass a `.json` file as an entrypoint to the bundler, Bun converts it to a `.js` module that `export default`s the parsed object.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```json  
      {
        "name": "John Doe",
        "age": 35,
        "email": "johndoe@example.com"
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default {
        name: "John Doe",
        age: 35,
        email: "johndoe@example.com",
      };
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `jsonc` [#jsonc]

**JSON with Comments loader**. Default for `.jsonc`.

JSONC (JSON with Comments) files can be directly imported. Bun parses them, stripping out comments and trailing commas.

```ts
import config from "./config.jsonc";
console.log(config);
```

During bundling, Bun inlines the parsed JSONC into the bundle as a JavaScript object, identical to the `json` loader.

```ts
var config = {
  option: "value",
};
```

<Note>
  Bun automatically uses the `jsonc` loader for `tsconfig.json`, `jsconfig.json`, `package.json`, and `bun.lock` files.
</Note>

### `toml` [#toml]

**TOML loader**. Default for `.toml`.

TOML files can be directly imported. Bun parses them with its fast native TOML parser.

```ts
import config from "./bunfig.toml";
config.logLevel; // => "debug"

// via import attribute:
// import myCustomTOML from './my.config' with {type: "toml"};
```

During bundling, Bun inlines the parsed TOML into the bundle as a JavaScript object.

```ts
var config = {
  logLevel: "debug",
  // ...other fields
};
config.logLevel;
```

If you pass a `.toml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```toml  
      name = "John Doe"
      age = 35
      email = "johndoe@example.com"
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default {
        name: "John Doe",
        age: 35,
        email: "johndoe@example.com",
      };
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `yaml` [#yaml]

**YAML loader**. Default for `.yaml` and `.yml`.

YAML files can be directly imported. Bun parses them with its fast native YAML parser.

```ts
import config from "./config.yaml";
console.log(config);

// via import attribute:
import data from "./data.txt" with { type: "yaml" };
```

During bundling, Bun inlines the parsed YAML into the bundle as a JavaScript object.

```ts
var config = {
  name: "my-app",
  version: "1.0.0",
  // ...other fields
};
```

If you pass a `.yaml` or `.yml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```yaml  
      name: John Doe
      age: 35
      email: johndoe@example.com
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default {
        name: "John Doe",
        age: 35,
        email: "johndoe@example.com",
      };
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `json5` [#json5]

**JSON5 loader**. Default for `.json5`.

JSON5 files can be directly imported. Bun parses them with its fast native JSON5 parser. JSON5 is a superset of JSON that adds comments, trailing commas, unquoted keys, single-quoted strings, and more.

```ts
import config from "./config.json5";
console.log(config);

// via import attribute:
import data from "./data.txt" with { type: "json5" };
```

During bundling, Bun inlines the parsed JSON5 into the bundle as a JavaScript object.

```ts
var config = {
  name: "my-app",
  version: "1.0.0",
  // ...other fields
};
```

If you pass a `.json5` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```json5  
      {
        // Configuration
        name: "John Doe",
        age: 35,
        email: "johndoe@example.com",
      }
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default {
        name: "John Doe",
        age: 35,
        email: "johndoe@example.com",
      };
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `xml` [#xml]

**XML loader**. Default for `.xml`.

XML files can be directly imported. Bun parses them with its native XML 1.0 parser into the compact object shape of [`Bun.XML.parse`](/runtime/xml):

* One key for the root element
* `"@name"` keys for attributes
* Arrays for repeated child elements
* `"#text"` for text next to attributes or children
* Every value is a string

```ts
import doc from "./config.xml";
console.log(doc.config["@version"]);

// via import attribute:
import feed from "./export.rss" with { type: "xml" };
```

During bundling, Bun inlines the parsed XML into the bundle as a JavaScript object.

```ts
var doc = {
  config: {
    "@version": "2",
    // ...other fields
  },
};
```

If you pass a `.xml` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the parsed object.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```xml  
      <user id="1">
        <name>John Doe</name>
        <email>johndoe@example.com</email>
        <role>admin</role>
        <role>editor</role>
      </user>
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default {
        user: {
          "@id": "1",
          name: "John Doe",
          email: "johndoe@example.com",
          role: ["admin", "editor"],
        },
      };
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `text` [#text]

**Text loader**. Default for `.txt` and `.text`.

Text files can be directly imported. Bun reads the file and returns it as a string.

```ts
import contents from "./file.txt";
console.log(contents); // => "Hello, world!"

// To import an html file as text
// The "type' attribute can be used to override the default loader.
import html from "./index.html" with { type: "text" };
```

When the file is referenced during a build, Bun inlines the contents into the bundle as a string.

```ts
var contents = `Hello, world!`;
console.log(contents);
```

If you pass a `.txt` file as an entrypoint, Bun converts it to a `.js` module that `export default`s the file contents.

<CodeGroup>
  <CodeBlockTabs defaultValue="Input" groupId="input+output">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Input">
        Input
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Output">
        Output
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Input">
      ```txt  
      Hello, world!
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Output">
      ```ts  
      export default "Hello, world!";
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

### `md` [#md]

**Markdown loader**. Default for `.md` and `.markdown`.

Markdown files can be directly imported. Bun renders the file to HTML and returns the HTML as a string.

```ts
import html from "./README.md";
console.log(html); // => "<h1>Title</h1>\n"

// via import attribute (`markdown` is an alias of `md`):
import notes from "./notes.txt" with { type: "md" };
```

During bundling, Bun inlines the rendered HTML into the bundle as a string.

### `napi` [#napi]

**Native addon loader**. Default for `.node`.

In the runtime, native addons can be directly imported.

```ts
import addon from "./addon.node";
console.log(addon);
```

In the bundler, Bun handles `.node` files using the [`file`](#file) loader.

### `sqlite` [#sqlite]

**SQLite loader**. `with { "type": "sqlite" }` import attribute

In the runtime and bundler, SQLite databases can be directly imported. Bun loads the database with [`bun:sqlite`](/runtime/sqlite).

```ts
import db from "./my.db" with { type: "sqlite" };
```

The `sqlite` loader is only supported when the `target` is `bun`.

By default, the database is external to the bundle: Bun doesn't bundle the on-disk database file into the final output, so you can use a database loaded elsewhere.

You can change this behavior with the `"embed"` attribute:

```ts
// embed the database into the bundle
import db from "./my.db" with { type: "sqlite", embed: "true" };
```

With a [standalone executable](/bundler/executables), Bun embeds the database into the single-file executable.

Otherwise, the database to embed is copied into the `outdir` with a hashed filename.

### `html` [#html]

The `html` loader processes HTML files and bundles any referenced assets. It:

* Bundles and hashes referenced JavaScript files (`<script src="...">`)
* Bundles and hashes referenced CSS files (`<link rel="stylesheet" href="...">`)
* Hashes referenced images (`<img src="...">`)
* Preserves external URLs (by default, anything starting with `http://` or `https://`)

For example, given this HTML file:

<CodeGroup>
  <CodeBlockTabs defaultValue="src/index.html" groupId="src-index-html">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="src/index.html">
        src/index.html
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="src/index.html">
      ```html  
      <!DOCTYPE html>
      <html>
        <body>
          <img src="./image.jpg" alt="Local image" />
          <img src="https://example.com/image.jpg" alt="External image" />
          <script type="module" src="./script.js"></script>
        </body>
      </html>
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

Bun outputs a new HTML file with the bundled assets:

<CodeGroup>
  <CodeBlockTabs defaultValue="dist/output.html" groupId="dist-output-html">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="dist/output.html">
        dist/output.html
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="dist/output.html">
      ```html  
      <!DOCTYPE html>
      <html>
        <body>
          <img src="./image-HASHED.jpg" alt="Local image" />
          <img src="https://example.com/image.jpg" alt="External image" />
          <script type="module" src="./output-ALSO-HASHED.js"></script>
        </body>
      </html>
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

The loader uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external.

The list of selectors is:

* `audio[src]`
* `img[src]`
* `img[srcset]`
* `link[as='font'][href], link[type^='font/'][href]`
* `link[as='image'][href]`
* `link[as='style'][href]`
* `link[as='video'][href], link[as='audio'][href]`
* `link[as='worker'][href]`
* `link[rel='icon'][href], link[rel='apple-touch-icon'][href]`
* `link[rel='manifest'][href]`
* `link[rel='stylesheet'][href]`
* `script[src]`
* `source[src]`
* `source[srcset]`
* `video[poster]`
* `video[src]`

<Note>
  **HTML Loader Behavior in Different Contexts**

  The `html` loader behaves differently depending on how it's used:

  1. **Static Build:** When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed.

  2. **Runtime:** When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement.

  3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production.
</Note>

### `css` [#css]

**CSS loader**. Default for `.css`.

CSS files can be directly imported. This is primarily useful when [bundling HTML](/bundler/html-static#importing-css-in-javascript), where CSS is bundled alongside HTML.

```ts
import "./styles.css";
```

The import returns no value; it's only used for its side effects.

### `sh` loader [#sh-loader]

**Bun Shell loader**. Default for `.sh` files

This loader parses [Bun Shell](/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime.

```sh
bun run ./script.sh
```

### `file` [#file]

**File loader**. Default for all unrecognized file types.

The file loader resolves the import as a *path/URL* to the imported file. It's commonly used for referencing media or font assets.

```ts title="logo.ts"
import logo from "./logo.svg";
console.log(logo);
```

*In the runtime*, Bun checks that the `logo.svg` file exists and resolves the import to its absolute path on disk.

```bash
bun run logo.ts
/path/to/project/logo.svg
```

*In the bundler*, Bun copies the file into `outdir` as-is, and the import resolves to a relative path pointing to the copied file.

```ts title="Output"
var logo = "./logo.svg";
console.log(logo);
```

If `publicPath` is set, the import uses its value as a prefix to construct an absolute path/URL.

| Public path                  | Resolved import                    |
| ---------------------------- | ---------------------------------- |
| `""` (default)               | `./logo.svg`                       |
| `"/assets/"`                 | `/assets/logo.svg`                 |
| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` |

<Note>
  The value of 

  [`naming.asset`](/bundler#naming)

   determines the location and file name of the copied file.
</Note>

<Accordion title="Fixing TypeScript import errors">
  If you're using TypeScript, you may get an error like this:

  ```ts
  // TypeScript error
  // Cannot find module './logo.svg' or its corresponding type declarations.
  ```

  To fix this, create a `*.d.ts` file anywhere in your project (any name works) with the following contents:

  ```ts
  declare module "*.svg" {
    const content: string;
    export default content;
  }
  ```

  This tells TypeScript to treat any default import from `.svg` as a string.
</Accordion>
