---
title: "Copy a directory into the output"
description: "Bring an existing file or directory tree into the output, templating text on the way."
source: "https://jostraca.org/how-to/copy-a-directory/"
---

# Copy a directory into the output

Bring an existing file or directory tree into the output, templating text on the way.

Rendered from [`docs/how-to/copy-a-directory.md`](https://github.com/jostraca/jostraca/blob/master/docs/how-to/copy-a-directory.md) in the generator repository — where a correction belongs, and where the test suite runs every example on this page or states why it does not.

`Copy` brings in a file or a whole tree. Text files pass through the template system, so `$$path$$` works in copied content; binaries are copied byte for byte.

The source tree is under `tpl/`. A templated asset, `tpl/assets/logo.svg`:

```html
<svg><!-- $$title$$ --></svg>
```

Something a level deeper, `tpl/assets/style/site.css`:

```css
body { font-family: sans-serif; }
```

And a single file to rename, `tpl/readme.txt`:

```text
# $$title$$
```

```js
import { Jostraca, Project, Folder, Copy } from 'jostraca'

const jostraca = Jostraca({ model: { title: 'My App' } })

await jostraca.generate({ folder: './out' }, () => {
  Project({ folder: 'app' }, () => {

    Folder({ name: 'static' }, () => {
      Copy({ from: './tpl/assets' })
    })

    Copy({ from: './tpl/readme.txt', to: 'README.txt' })
  })
})
```

```text
app/README.txt
app/static/logo.svg
app/static/style/site.css
```

The tree keeps its shape, and `$$title$$` was substituted in `logo.svg`:

```html
<svg><!-- My App --></svg>
```

`to` renames a single file and names a subfolder for a directory. Both may contain slashes.

The one asymmetry to remember: **a relative `from` resolves against the process working directory**, not against the output folder the way `Fragment`’s `from` does. In a generator you publish, build an absolute path from `import.meta.url` rather than relying on where the user happened to be standing.

Text or binary is decided by the extension first, against a fixed list of around 250. Because no such list is complete, the content is then sniffed: a NUL byte in the first 8192 promotes an unlisted file to binary, which is what keeps `.wasm` and extensionless files intact.

## See also

-   [Skip files when copying](https://jostraca.org/how-to/skip-files-when-copying).
-   [Component reference](https://jostraca.org/docs/reference-components#copy) for every prop.
