{"id":16616300,"url":"https://github.com/cbothner/react-activestorage-provider","last_synced_at":"2025-04-05T23:06:55.433Z","repository":{"id":29358308,"uuid":"120817192","full_name":"cbothner/react-activestorage-provider","owner":"cbothner","description":"A React component that allows easy file upload using ActiveStorage","archived":false,"fork":false,"pushed_at":"2023-12-20T03:09:48.000Z","size":578,"stargazers_count":112,"open_issues_count":31,"forks_count":36,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-24T08:24:23.478Z","etag":null,"topics":["activestorage","file-upload","rails","react"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cbothner.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-08T20:58:14.000Z","updated_at":"2024-06-18T16:49:26.033Z","dependencies_parsed_at":"2024-06-18T16:59:18.504Z","dependency_job_id":null,"html_url":"https://github.com/cbothner/react-activestorage-provider","commit_stats":{"total_commits":66,"total_committers":6,"mean_commits":11.0,"dds":"0.18181818181818177","last_synced_commit":"6be6084e90552bec86a92d3be914d0eca7739b80"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbothner%2Freact-activestorage-provider","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbothner%2Freact-activestorage-provider/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbothner%2Freact-activestorage-provider/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbothner%2Freact-activestorage-provider/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cbothner","download_url":"https://codeload.github.com/cbothner/react-activestorage-provider/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411231,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activestorage","file-upload","rails","react"],"created_at":"2024-10-12T02:12:34.351Z","updated_at":"2025-04-05T23:06:55.416Z","avatar_url":"https://github.com/cbothner.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-activestorage-provider\n\n[![NPM](https://img.shields.io/npm/v/react-activestorage-provider.svg)](https://www.npmjs.com/package/react-activestorage-provider)\n[![Build Status](https://semaphoreci.com/api/v1/cbothner/react-activestorage-provider/branches/master/shields_badge.svg)](https://semaphoreci.com/cbothner/react-activestorage-provider)\n[![CodeClimate Maintainability](https://img.shields.io/codeclimate/maintainability/cbothner/react-activestorage-provider.svg)](https://codeclimate.com/github/cbothner/react-activestorage-provider/maintainability)\n[![CodeClimate Coverage](https://img.shields.io/codeclimate/coverage/cbothner/react-activestorage-provider.svg)](https://codeclimate.com/github/cbothner/react-activestorage-provider/coverage)\n\nActiveStorage is an amazing addition to Rails 5.2, and as usual the team have made it fantastically simple to use... if you’re generating HTML server-side, that is. This component aims to make it just as easy to use from React.\n\nActiveStorageProvider handles the direct upload of a file to an ActiveStorage service and the attachment of that file to your model. It uses the render props pattern so you can build your own upload widget.\n\n## Install\n\n```bash\nnpm install --save react-activestorage-provider\n```\n\n## Usage\n\nActiveStorageProvider makes it easy to add a simple upload button. When you call `handleUpload` with a `FileList` or an array of `File`s, this component creates a `Blob` record, uploads the file directly to your storage service, and then hits your Rails controller to attach the blob to your model. (If you want to handle the attachment yourself in order to, for example, provide other attributes, [see the lower level `DirectUploadProvider`](#directuploadprovider).)\n\n```jsx\nimport ActiveStorageProvider from 'react-activestorage-provider'\n\n// ...\n\nreturn (\n  \u003cActiveStorageProvider\n    endpoint={{\n      path: '/profile',\n      model: 'User',\n      attribute: 'avatar',\n      method: 'PUT',\n    }}\n    onSubmit={user =\u003e this.setState({ avatar: user.avatar })}\n    render={({ handleUpload, uploads, ready }) =\u003e (\n      \u003cdiv\u003e\n        \u003cinput\n          type=\"file\"\n          disabled={!ready}\n          onChange={e =\u003e handleUpload(e.currentTarget.files)}\n        /\u003e\n\n        {uploads.map(upload =\u003e {\n          switch (upload.state) {\n            case 'waiting':\n              return \u003cp key={upload.id}\u003eWaiting to upload {upload.file.name}\u003c/p\u003e\n            case 'uploading':\n              return (\n                \u003cp key={upload.id}\u003e\n                  Uploading {upload.file.name}: {upload.progress}%\n                \u003c/p\u003e\n              )\n            case 'error':\n              return (\n                \u003cp key={upload.id}\u003e\n                  Error uploading {upload.file.name}: {upload.error}\n                \u003c/p\u003e\n              )\n            case 'finished':\n              return (\n                \u003cp key={upload.id}\u003eFinished uploading {upload.file.name}\u003c/p\u003e\n              )\n          }\n        })}\n      \u003c/div\u003e\n    )}\n  /\u003e\n)\n```\n\n### `ActiveStorageProvider` Props\n\nThese are your options for configuring ActiveStorageProvider.\n\n| Prop (\\*required)        | Description                                                                                                                                                               |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `directUploadsPath`      | `string`\u003cbr /\u003eThe direct uploads path on your Rails app, if you’ve overridden `ActiveStorage::DirectUploadsController`                                                    |\n| `endpoint`\\*             | `{ path: string, model: string, attribute: string, method: string, host?: string, port?: string, protocol?: string }`\u003cbr /\u003eThe details for the request to attach the file |\n| `headers`                | `{[key: string]: string}`\u003cbr/\u003eOptional headers to add to request, can also be used to override default headers                                                            |\n| `multiple`               | `boolean` (false)\u003cbr/\u003eWhether the component should accept multiple files. If true, the model should use `has_many_attached`                                               |\n| `onBeforeBlobRequest`    | `({ id: string, file: File, xhr: XMLHttpRequest }) =\u003e mixed`\u003cbr /\u003eA callback that allows you to modify the blob request                                                   |\n| `onBeforeStorageRequest` | `({ id: string, file: File, xhr: XMLHttpRequest }) =\u003e mixed`\u003cbr /\u003eA callback that allows you to modify the storage request                                                |\n| `onError`                | `Response =\u003e mixed`\u003cbr /\u003eA callback to handle an error (\u003e= 400) response by the server in saving your model                                                               |\n| `onSubmit`\\*             | `Object =\u003e mixed`\u003cbr /\u003eA callback for the server response to successfully saving your model                                                                               |\n| `render`\\*               | `RenderProps =\u003e React.Node`\u003cbr /\u003eRender props                                                                                                                             |\n\n### `RenderProps`\n\nThis is the type of the argument with which your render function will be called.\n\n```jsx\nexport type RenderProps = {\n  ready: boolean /* false while any file is uploading */,\n  uploads: ActiveStorageFileUpload[] /* uploads in progress */,\n\n  handleUpload: (FileList | File[]) =\u003e mixed /* call to initiate an upload */,\n\n  /* or, if you want more granular control... */\n\n  /* call to set list of files to be uploaded */\n  handleChooseFiles: (FileList | File[]) =\u003e mixed,\n  /* then call to begin the upload of the files in the list */\n  handleBeginUpload: () =\u003e mixed,\n}\n\ntype ActiveStorageFileUpload =\n  | { state: 'waiting', id: string, file: File }\n  | { state: 'uploading', id: string, file: File, progress: number }\n  | { state: 'error', id: string, file: File, error: string }\n  | { state: 'finished', id: string, file: File }\n```\n\n## `DirectUploadProvider`\n\nActiveStorageProvider makes it simple to add a quick “upload” button by taking care of both uploading and attaching your file, but it shouldn’t stand in your way if you’re doing something more interesting. If you want to handle the second step, attaching your `Blob` record to your model, yourself, you can use the lower level `DirectUploadProvider`. It creates the blob records and uploads the user’s files directly to your storage service, then calls you back with the signed ids of those blobs. Then, you can create or update your model as you need.\n\n```jsx\nfunction PostForm() {\n  function handleAttachment(signedIds) {\n    const body = JSON.stringify({ post: { title: ..., images: signedIds }})\n    fetch('/posts.json', { method: 'POST', body })\n  }\n\n  return (\n    \u003cDirectUploadProvider multiple onSuccess={handleAttachment} render={...} /\u003e\n  )\n}\n```\n\n`DirectUploadProvider` is a named export, so\n\n```jsx\nimport { DirectUploadProvider } from 'react-activestorage-provider'\n```\n\nand use it with the following props:\n\n| Prop (\\*required)        | Description                                                                                                                             |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |\n| `directUploadsPath`      | `string`\u003cbr /\u003eThe direct uploads path on your Rails app, if you’ve overridden `ActiveStorage::DirectUploadsController`                  |\n| `headers`                | `{[key: string]: string}`\u003cbr/\u003eOptional headers to add to request                                                                        |\n| `multiple`               | `boolean`\u003cbr/\u003eWhether the component should accept multiple files. If true, the model should use `has_many_attached`                     |\n| `onBeforeBlobRequest`    | `({ id: string, file: File, xhr: XMLHttpRequest }) =\u003e mixed`\u003cbr /\u003eA callback that allows you to modify the blob request                 |\n| `onBeforeStorageRequest` | `({ id: string, file: File, xhr: XMLHttpRequest }) =\u003e mixed`\u003cbr /\u003eA callback that allows you to modify the storage request              |\n| `onSuccess`\\*            | `(string[]) =\u003e mixed`\u003cbr /\u003eThe callback that will be called with the signed ids of the files after the upload is complete               |\n| `origin`                 | `{ host?: string, port?: string, protocol?: string }`\u003cbr /\u003eThe origin of your rails server. Defaults to where your React app is running |\n| `render`\\*               | `RenderProps =\u003e React.Node`\u003cbr /\u003eRender props                                                                                           |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbothner%2Freact-activestorage-provider","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcbothner%2Freact-activestorage-provider","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbothner%2Freact-activestorage-provider/lists"}