# File attachments!

**URL:** <https://talk.observablehq.com/t/file-attachments/2499>\
**Category:** Announcements\
**Created:** [October 30, 2019, 7:48pm UTC](https://talk.observablehq.com/t/file-attachments/2499 "2019-10-30T19:48:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [October 30, 2019, 7:48pm UTC](https://talk.observablehq.com/t/file-attachments/2499/1 "2019-10-30T19:48:02Z")

</div>

We just deployed and [announced](https://twitter.com/observablehq/status/1189628860926545921) a new feature: **file attachments**!

> **[File Attachments](https://observablehq.com/@observablehq/file-attachments)**
>
> Files, IRL. Photo: EduVolunteer/Wikimedia The quickest way to work with small-ish datasets in a notebook is to attach a file. Files in Observable can be used for a variety of purposes besides data too, such as videos or images (like the one above!)....

You can now upload and attach files directly to notebooks, making it much easier to work with small-ish datasets. We’ve updated many of our example notebooks to use this new feature.

Please try it out and let us know what you think! 🙏

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [October 30, 2019, 8:42pm UTC](https://talk.observablehq.com/t/file-attachments/2499/2 "2019-10-30T20:42:45Z")

</div>

Can you share more details on the rolling quota?

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [October 30, 2019, 8:50pm UTC](https://talk.observablehq.com/t/file-attachments/2499/3 "2019-10-30T20:50:59Z")

</div>

Not currently. We’ll be evaluating the rolling quota based on usage.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [October 30, 2019, 8:53pm UTC](https://talk.observablehq.com/t/file-attachments/2499/4 "2019-10-30T20:53:50Z")

</div>

Is it a space or bandwidth quota? If I should ever hit the quota, can I just delete files? And does the storage backend deduplicate artifacts (like git LFS)?

_Edit: I just realized that responding to “not currently” with the above questions may appear ignorant, so please let me add the following introduction:_

If you can’t share details on the imposed limits, perhaps you can still shed some light on the following questions? […]

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [October 30, 2019, 9:10pm UTC](https://talk.observablehq.com/t/file-attachments/2499/5 "2019-10-30T21:10:06Z")

</div>

~~@mbostock I just pasted a URL into a cell and it got automatically turned into an attachment. This was absolutely not my intention. Please disable this “feature” or make it optional.~~

Nevermind, I’m dumb. Had the image in the clipboard instead of the URL. Nice feature! 👍

**Edit:** However, I’d still love to see a setting to disable this feature or a confirmation dialog. Removing an accidentally added attachment currently requires _six_ interaction steps.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [October 30, 2019, 10:15pm UTC](https://talk.observablehq.com/t/file-attachments/2499/6 "2019-10-30T22:15:56Z")

</div>

The paste feature still needs polish. Here’s the result of keeping cmd+v pressed for approx. two seconds, while an image (2606x3240, [link](https://images.unsplash.com/photo-1570649857669-4ad9f896435d?ixlib=rb-1.2.1&auto=format&fit=crop&w=2606&q=80)) was in the clipboard:

- notebook freezes while 37 OPTIONS requests are made, in intervals of ~7 seconds.
- after ~ 2 min: notebook becomes responsive again, upload starts. Displayed progress appears to switch between multiple files (goes back and forth).
- after ~5 min: notebook crashes with an uncaught error inside a progress event, apparently triggered by a CORS error (hit the quota?).
- after reloading: 32 files were added. Deleting a single file requires three clicks.

https://player.vimeo.com/video/369949422

---

<div class="post-metadata">

**Author:** ![tom](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tom/32/2262_2.png) [@tom](https://talk.observablehq.com/u/tom)\
**Post date:** [October 30, 2019, 10:49pm UTC](https://talk.observablehq.com/t/file-attachments/2499/7 "2019-10-30T22:49:25Z")

</div>

We’ll look into showing progress UI faster when you hit paste. In the meantime we recommend not holding down paste - like GitHub, this forum, and any text field, holding down paste keeps pasting.

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [October 30, 2019, 11:20pm UTC](https://talk.observablehq.com/t/file-attachments/2499/8 "2019-10-30T23:20:56Z")

</div>

> [@tom](#):
>
> In the meantime we recommend not holding down paste

My intention was to demonstrate how little of an effort is needed to cause results that may take significantly longer to clean up. Some notebooks make use of the clipboard, and in these the right or wrong focus makes all the difference.

I’d suggest the following additions:

- don’t respond to repeated pastes of the same blob until the first instance has been uploaded and mapped,
- hash blobs and map them to existing files via hashes (would require roundtrip before post),
- allow bulk deletion of files.

---

<div class="post-metadata">

**Author:** ![jrus](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/jrus/32/586_2.png) [@jrus](https://talk.observablehq.com/u/jrus)\
**Post date:** [October 31, 2019, 11:57am UTC](https://talk.observablehq.com/t/file-attachments/2499/9 "2019-10-31T11:57:27Z")

</div>

Great new feature folks! 👍

* * *

Can files be renamed? It seems a bit limiting to need to pick the name on disk before upload.

* * *

Future feature request: use the same system for uploaded notebook thumbnails.

* * *

~~Tiny note: in the docs, the contents list at the top includes a link to [GitHub - observablehq/stdlib: The Observable standard library.](https://github.com/observablehq/stdlib#file_attachments) while the anchor link down the page uses a hyphen instead of an underscore [GitHub - observablehq/stdlib: The Observable standard library.](https://github.com/observablehq/stdlib#file-attachments)~~🛠 FIXED

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [October 31, 2019, 2:09pm UTC](https://talk.observablehq.com/t/file-attachments/2499/10 "2019-10-31T14:09:30Z")

</div>

> [@jrus](#):
>
> Can files be renamed?

Not currently, sorry! We’ll likely add this in the future.

One wrinkle is that files are currently immutable, so “renaming” would create a new alias for that file and replace references to the old name with the new name. However, we would eventually like to allow file mutability while preserving notebook history, so stay tuned.

---

<div class="post-metadata">

**Author:** ![j-f1](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/j-f1/32/368_2.png) [@j-f1](https://talk.observablehq.com/u/j-f1)\
**Post date:** [October 31, 2019, 9:44pm UTC](https://talk.observablehq.com/t/file-attachments/2499/11 "2019-10-31T21:44:10Z")

</div>

One option would be to do it in a way similar to Git, where you have each version reference the hash of each attachment:

```auto
// in the notebook data
"files": {
  "foo.txt": "9206ac42b532ef8e983470c251f4e1a365fd636c",
  "bar.json": "e706dccea74c3b564eda845fed6e2e725f680a91"
}

```

and you could download the files from the CDN by hash.

This would allow updating files by changing the hash in the new version (and uploading the new file to the CDN) and attaching the new file. Users could also go back in the history and delete the files, which would delete them from the CDN and cause the `FileAttachment` call to fail when inspecting previous versions.

This would also allow renaming files since you could point a new name to the same hash in a subsequent version.

---

<div class="post-metadata">

**Author:** ![jipexu](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/jipexu/32/1312_2.png) [@jipexu](https://talk.observablehq.com/u/jipexu)\
**Post date:** [November 4, 2019, 10:28am UTC](https://talk.observablehq.com/t/file-attachments/2499/12 "2019-11-04T10:28:05Z")

</div>

Great feature !  
I have probably a stupid question …  
if you download the code of a notebook with some file attached, to your serveur :  
is this file dowloaded to the server ?  
if this file is updated on server side (for example by php from mysql query)

the final result of the js will take the change ??

Thanks

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [November 4, 2019, 6:01pm UTC](https://talk.observablehq.com/t/file-attachments/2499/13 "2019-11-04T18:01:41Z")

</div>

If you download your notebook’s tarball, the tarball will include all of your notebook’s files (and any transitive import’s files, too). FileAttachment will pass a relative path to fetch to load the file from your server. The file names are hashes, but if you look at the generated code you can determine which hashes corresponds to which file names.

You can replace the downloaded file in the tarball with different contents, or you can edit the code to point to a different file, as you prefer.

---

<div class="post-metadata">

**Author:** ![jipexu](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/jipexu/32/1312_2.png) [@jipexu](https://talk.observablehq.com/u/jipexu)\
**Post date:** [November 4, 2019, 7:16pm UTC](https://talk.observablehq.com/t/file-attachments/2499/14 "2019-11-04T19:16:00Z")

</div>

ok many thanks Mike its great !

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [November 5, 2019, 10:41pm UTC](https://talk.observablehq.com/t/file-attachments/2499/15 "2019-11-05T22:41:27Z")

</div>

I’ve been disconnected from the Internet for most of the past month, and therefore slow to realize the impact of this feature: it’s a huge deal. Thank you! It’s really nice to be able to work with data files without having to find a way to host and link them outside Observable. 🥳 While Observable, at launch, helped reduce the barriers to entry for me learning JS (and this community helps so much too), the many innovations you’ve made in the short time since launch have further eliminated several of the sticking points that originally stymied me. I can’t express enough my gratitude for this gift.

Thank you so much!

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [November 5, 2019, 11:07pm UTC](https://talk.observablehq.com/t/file-attachments/2499/16 "2019-11-05T23:07:54Z")

</div>

Thanks @aaronkyle! We appreciate you sticking with us.

---

<div class="post-metadata">

**Author:** ![maliky](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/maliky/32/706_2.png) [@maliky](https://talk.observablehq.com/u/maliky)\
**Post date:** [November 9, 2019, 6:54pm UTC](https://talk.observablehq.com/t/file-attachments/2499/17 "2019-11-09T18:54:22Z")

</div>

Thank you @aaronkyle for expressing what I feel too towards the observable team :o)

Concerning the file attachements, beside the announcement, I noticed the change in an unfortunate way.

I had successfully bundled a notebook referring to [@jashkenas/inputs](https://observablehq.com/@jashkenas/inputs) but since the header image was added as a file attachement `src="${await FileAttachment("capstan.gif").url()}" ` I failed because I need to do some additionnal configuration with webpack.

The error message of `yarn build` is the following.

```bash
ERROR in ./node_modules/@maliky/3-filtres-pour-les-donnees-des-mooc/e93997d5089d7165@2200.js 4:203
Module parse failed: Unexpected token (4:203)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| export default function define(runtime, observer) {
| const main = runtime.module();
> const fileAttachments = new Map([["capstan.gif",new URL("./files/c051fbc024553912e31968b35e537d4ad3592201b5f8e7bd13fd9d02e38599c5d541a704d0858c676328babb3e5c9c35dd7c6d67240090d094882a1cad8eece4",import.meta.url)]]);
| main.builtin("FileAttachment", runtime.fileAttachments(name => fileAttachments.get(name)));
| main.variable(observer()).define(["md","FileAttachment"], async function(md,FileAttachment){return(
 @ ./node_modules/@maliky/3-filtres-pour-les-donnees-des-mooc/482865d740b971e4@946.js 3:0-49 414:32-39
 @ ./static/rdb/js/src/collapse.index.js
error Command failed with exit code 2.

```

The `e93997d5089d7165@2200.js ` is [Jemery’s inputs notebook](https://observablehq.com/@jashkenas/inputs). and the error is related to webpack not been able to handle the file attachement type.

I’ve juste started to look into a solution to this and install bable loader for markdown, file and images (including gif) but something else is blocking the bundling process. Does it have to do with the Map or URL keyword ?

I’m sure that once I’ll have resolved this webpack configuration issue I get a lot of benefits from file attachements.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [November 9, 2019, 10:23pm UTC](https://talk.observablehq.com/t/file-attachments/2499/18 "2019-11-09T22:23:42Z")

</div>

Does webpack [support import.meta](https://github.com/webpack/webpack/issues/6719)? You might need this plugin, or similar:

> <https://github.com/open-wc/open-wc/blob/master/packages/webpack-import-meta-loader/webpack-import-meta-loader.js>

---

<div class="post-metadata">

**Author:** ![maliky](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/maliky/32/706_2.png) [@maliky](https://talk.observablehq.com/u/maliky)\
**Post date:** [November 10, 2019, 6:41pm UTC](https://talk.observablehq.com/t/file-attachments/2499/19 "2019-11-10T18:41:25Z")

</div>

I was stuck trying to set-up two loaders: ‘babel-loader’ and ‘webpack-import-meta-loader’ for the same ‘js’ file in webpack.config.js

So I also looked at `@babel/plugin-syntax-import-meta` but installing it and adding the adequate line in for `.babelrc` was not enough.

I came back to the webpack.config.js and just added the two loaders one after another as in :

```javascript
 module: {
   rules: [
     { test: /\.css$/, use: "css-loader" },
     {
       test: /\.js$/,
       loader: require.resolve("@open-wc/webpack-import-meta-loader")
     },
     {
       test: /.(js|jsx)$/,
       include: [path.resolve(__dirname, "static/rdb/js/src")],
       use: {
         loader: "babel-loader"
       }
     }
   ]
 }

```

And it works…  
Thank you

---

<div class="post-metadata">

**Author:** ![sourqe](https://avatars.discourse-cdn.com/v4/letter/s/3ab097/32.png) [@sourqe](https://talk.observablehq.com/u/sourqe)\
**Post date:** [December 22, 2019, 2:19pm UTC](https://talk.observablehq.com/t/file-attachments/2499/20 "2019-12-22T14:19:58Z")

</div>

@maliky Could you perhaps share a repository that you used to get everything in the regular react app working? I tried to implement the various things you mentioned to fix the problem, but I still have the issue.

[Next page](https://talk.observablehq.com/t/file-attachments/2499.md?page=2)
