{"id":13929091,"url":"https://github.com/gristlabs/grist-static","last_synced_at":"2025-04-05T20:01:52.021Z","repository":{"id":136464586,"uuid":"608453770","full_name":"gristlabs/grist-static","owner":"gristlabs","description":"Showing Grist spreadsheets on a static website, without a special backend.","archived":false,"fork":false,"pushed_at":"2025-03-03T20:24:43.000Z","size":1444,"stargazers_count":113,"open_issues_count":4,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-29T19:01:49.611Z","etag":null,"topics":["spreadsheet","static-site"],"latest_commit_sha":null,"homepage":"https://gristlabs.github.io/grist-static/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gristlabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":null,"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},"funding":{"github":"gristlabs"}},"created_at":"2023-03-02T03:29:03.000Z","updated_at":"2025-03-23T15:03:55.000Z","dependencies_parsed_at":"2025-02-16T23:09:50.770Z","dependency_job_id":"560237a8-f1e1-40d4-b3eb-e3ecab8caab9","html_url":"https://github.com/gristlabs/grist-static","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gristlabs%2Fgrist-static","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gristlabs%2Fgrist-static/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gristlabs%2Fgrist-static/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gristlabs%2Fgrist-static/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gristlabs","download_url":"https://codeload.github.com/gristlabs/grist-static/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247393547,"owners_count":20931811,"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":["spreadsheet","static-site"],"created_at":"2024-08-07T18:02:06.524Z","updated_at":"2025-04-05T20:01:51.983Z","avatar_url":"https://github.com/gristlabs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/gristlabs"],"categories":["others"],"sub_categories":[],"readme":"# grist-static: Grist on static sites without embeds\n\nThis is a way to view and interact with `.grist` files\n(Grist spreadsheets) on regular websites, with no special back-end support needed.\nThe idea here is that it would be great for making reports if we could put\nspreadsheets on a website like we do PDFs, with nice formatting\nand navigation options and not much fuss.\n\nIf you *can* run a special back-end,\n[grist-core](https://github.com/gristlabs/grist-core) is the most\nbattle-tested way to host Grist spreadsheets.\nAnd for many purposes [Grist embedding](https://support.getgrist.com/embedding/)\nmay be adequate, where your embed a Grist spreadsheet\nfrom an external Grist installation (such as the hosted service offered by\nGrist Labs). But if you cannot host your data externally, and don't want\nthe operational burden of standing up a Grist installation of your own,\n`grist-static` gives you a way to easily render Grist spreadsheets\non regular websites.\nLike a PDF, people will be able to view the spreadsheet, navigate\naround in it, and search within it. Better than a PDF, they'll be\nable to change selections, and experiment with changing numbers to\nsee what happens. Every viewer has their own copy, and their changes\nwon't be seen by others, or stored.\nThis would also be a scalable way to show a Grist document to\nmillions of simultaneous users.\n\n## See an example\n\nhttps://gristlabs.github.io/grist-static shows a couple of\nexample Grist documents hosted on GitHub Pages:\n\nhttps://user-images.githubusercontent.com/118367/227564527-82a9929c-40d3-4167-999f-6aeee4955723.mp4\n\n## Serving a Grist document on a static website\n\nThese days, PDFs can be placed on a website, and you can link to them with the expectation that browsers will show them nicely.\nBrowsers don't have native support for Grist [yet :-)] but you can make a little wrapper page like this:\n\n```html\n\u003c!doctype html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf8\"\u003e\n    \u003cscript src=\"https://grist-static.com/latest.js\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cscript\u003e\n      bootstrapGrist({\n        initialFile: 'investment-research.grist',\n        name: 'Investment Research',\n      });\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n  * The code in this repository is available from https://grist-static.com/ as a CDN; you can produce it all yourself using this repo.\n  * After that, it is just a case of putting a `.grist` file on your server beside this `.html` file, and filing in the options to `bootstrapGrist`.\n  * You can also pass `initialData: 'path/to/data.csv'` to import a CSV file into a new table. In this case `initialFile` is optional.\n  * There is also `initialContent` option. You can use it to pass the content of a CSV file.\n  * You can also pass `elementId: 'element-id` to open Grist within an element in your page.\n\t- In that case, you can include a small wrapper page for your document as above, and embed it as an iframe yourself.\n  * You can set `singlePage: true` for a less busy, single page layout.\n\n## Grist CSV Viewer\n\nGrist can handle data in multiple formats, including CSV. Since CSV is such a common format, and interacting with it\nonline remains a chore, we've packaged `grist-static` into a streamlined `csv-viewer.js` utility specifically for\nviewing, sorting, filtering, any copy/pasting from CSV, directly in a webpage.\n\nJust add the viewer in the `head` area of a webpage:\n\n```html\n\u003chead\u003e\n  \u003cscript src=\"https://grist-static.com/csv-viewer.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n```\n\nThen you can make a button to open CSV from a URL:\n```html\n\u003cbutton data-grist-csv-open=\"transactions.csv\"\u003eView CSV\u003c/button\u003e\n```\nThe CSV could be a file, or a URL of CSV data that your site generates for a user dynamically.\n\nIf you are working with links rather than buttons, the same approach works:\n\n```html\n\u003ca data-grist-csv-open=\"transactions.csv\" href=\"#\"\u003eView CSV\u003c/a\u003e\n```\n\nYou can of course style the buttons and links as you wish.\n\n## Grist CSV Viewer as a web component\n\nThe CSV Viewer can also be used as a web component called `csv-viewer`:\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript src=\"https://grist-static.com/csv-viewer.js\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ccsv-viewer initial-data=\"path/to/data.csv\" style=\"height: 500px; border: 1px solid green\"\u003e\n    \u003c/csv-viewer\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe component in fact accepts the same set of options as the `bootstrapGrist` function, so you can configure it to show either a CSV file or (despite its name) a Grist document of your choice. Full list of options available:\n\n- `initial-file`: The initial Grist document to load.\n- `initial-data`: A CSV file to import.\n- `initial-content`: A content of a CSV file to import.\n- `name`: The name of the Grist document to use.\n- `single-page`: A boolean attribute that, when present, displays the document in a less busy, single page layout.\n- `loader`: A boolean attribute that, when present, displays a loading spinner while the document is loading.\n\n## More viewer options\n\nWe've seen that with `csv-viewer.js`, any element can be converted to a link that opens a popup with a CSV file. All you need to do is to add `data-grist-csv-open` attribute to it.\n\nThere are other options available.\n\n  * Setting `data-grist-doc-open` allows opening a Grist document rather than a CSV (despite the utility's name).\n  * Set `data-single-page` to `true` for a single page layout, or `false` for a multi-page layout.\n  * Use `data-name` to override the default document name shown in the multi-page layout.\n  * Use `data-loader` to show a loading spinner while the document is loading. This is enabled by default, pass `data-loader=\"false\"` to disable it.\n\nThere are also some predefined button classes, specifically `grist`\nand `grist-large`, that offer Grist's default styling.\n\nFor finer control, there is a global `previewInGrist` function with the same API as `bootstrapGrist`,\nbut instead of rendering inline it opens a preview in a popup. This might be useful for any dynamically created content or files that are not available at the time of page load.\n\n## Differences with regular Grist\n\n * Changes aren't stored.\n * Changes are not shared with other viewers.\n * No specific access control.\n * Very immature, some features not yet ported, such as:\n   - Attachments\n\t - Should be doable (e.g. via service worker)\n   - Importing and exporting\n     - Again, doable, just \"different\"\n   - Data size measurement\n     - A needed configuration option on compiling SQLite.\n\n## Tips for small .grist files\n\nGrist spreadsheets by default store a lot of history in the `.grist` file.\nYou can prune that history by building `grist-core` and then, in the\n`grist-core` directory, doing:\n\n```\nyarn run cli history prune PATH/TO/YOUR/doc.grist\n```\n\nSorry this is awkward.\n\n## Building from source\n\n```\ngit submodule update --init\nmake requirements\nmake build\nmake serve\n# now visit http://localhost:3030/page/\n```\n\nThe sequence above places a lot of links in the `page`\ndirectory, for convenience during development. To collect\nfiles for uploading, use instead:\n\n```\nmake package\n# everything you need is in dist/\n```\n\n## Serving all files from your own website or CDN\n\nAll HTML examples so far have used `https://grist-static.com/`,\na domain operated by Grist Labs that only serves static files.\nThis domain logs traffic to measure usage, but does not set or track cookies.\nYou can copy all needed files to your own website or CDN to keep your\ntraffic completely private.\n\nYou can get the files needed by:\n\n * Building from source.\n * Or by running `npm pack grist-static` to fetch the latest tarball from the NPM registry.\n * Or by visiting https://registry.npmjs.org/grist-static/latest and finding a link to the latest tarball, then downloading it.\n\nTarballs (`.tgz` files) are a common archive format, with many free\ntools available for unpacking them.\nHowever you get there, in the end you will have a `dist/` directory\ncontaining `csv-viewer.js`, `latest.js`, and many other\nfiles that make up Grist. Place that material where you wish,\nand update your URLs appropriately.\n\nThe `jsdelivr` CDN automatically mirrors packages placed on NPM,\nso let's use it as an example. We could replace:\n\n```html\n\u003cscript src=\"https://grist-static.com/csv-viewer.js\"\u003e\u003c/script\u003e\n```\n\nwith:\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/grist-static@latest/dist/csv-viewer.js\"\u003e\u003c/script\u003e\n```\n\nOf course, this particular change would simply move usage information\nto `jsdelivr.net` rather than `grist-static.com`, but you get\nthe idea. Just use the same pattern for wherever you choose to place\nthe files.\n\n## Roadmap\n\nIt could be neat to make user changes persist in their browser -\n[OPFS](https://sqlite.org/wasm/doc/tip/persistence.md#opfs)\nmay be a good option for that, once it has broad browser support.\nOther steps:\n\n * [X] Get something that works on a webserver without special COOP/COEP headers.\n * [ ] Start making versioned .zip releases of all needed assets.\n * [X] Get a few small tweaks to enable plugging in alternate storage and build steps landed upstream in `grist-core`.\n * [ ] Support attachments - maybe serve as data URIs? Might need a service worker if separate URLs are unavoidable.\n * [ ] Whittle down the code and clean up the demo now we know what we're doing.\n * [ ] Hide parts of UI that don't make sense in this context.\n * [ ] Consider switching to SQLite developers' version of sqlite.js, which has good local storage support.\n * [ ] Enable at least one export option (Download *.grist seems easiest).\n * [ ] Enable at least one import option.\n * [ ] Give a better way of pruning *.grist files.\n * [ ] Currently, if a *.grist file is old relative to the code, and a migration needs to run, the \"data engine\"\n   may end up on the critical path to showing the document (and that remains the case indefinitely since the result\n   of migration cannot be stored). The data engine is relatively slow to start up, compared to the rest of the\n   app, so this is sad. Look at ways around this.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgristlabs%2Fgrist-static","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgristlabs%2Fgrist-static","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgristlabs%2Fgrist-static/lists"}