{"id":16096157,"url":"https://github.com/gordonsmith/observablehq-blocks","last_synced_at":"2025-04-05T20:28:15.604Z","repository":{"id":141598823,"uuid":"565182942","full_name":"GordonSmith/observablehq-blocks","owner":"GordonSmith","description":"ObservableHQ JavaScript in your README.md","archived":false,"fork":false,"pushed_at":"2022-11-12T15:45:56.000Z","size":7,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-11T19:38:36.606Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GordonSmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2022-11-12T15:45:54.000Z","updated_at":"2022-11-12T15:46:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"e19852ae-ea7a-4d0a-8678-98933eb3ba03","html_url":"https://github.com/GordonSmith/observablehq-blocks","commit_stats":{"total_commits":1,"total_committers":1,"mean_commits":1.0,"dds":0.0,"last_synced_commit":"0c090bc7d018c59380fdf410316918c24a2e6b2f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":"githubnext/blocks-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GordonSmith%2Fobservablehq-blocks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GordonSmith%2Fobservablehq-blocks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GordonSmith%2Fobservablehq-blocks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GordonSmith%2Fobservablehq-blocks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GordonSmith","download_url":"https://codeload.github.com/GordonSmith/observablehq-blocks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247396803,"owners_count":20932409,"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":[],"created_at":"2024-10-09T17:11:21.791Z","updated_at":"2025-04-05T20:28:15.550Z","avatar_url":"https://github.com/GordonSmith.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Blocks Template\n\n\u003e 📣 Use this repository as a starter template if you're a GitHub user interested in building your own custom Blocks!\n\nThis project contains [React](https://reactjs.org/) components written in\n[Typescript](https://www.typescriptlang.org/) and bundled with\n[Vite](https://vitejs.dev/). Read on to learn how to build Blocks with it!\n\nOr watch the video:\n\nhttps://user-images.githubusercontent.com/56439/186799325-41602d5b-708b-4905-b3a3-02f50ef52425.mp4\n\n## What are blocks?\n\nBlocks allow you to extend GitHub's interface in some pretty powerful ways! It could be as simple as a custom renderer for files or folders in your repository, and it can be as flexible as a full interface for editing content.\n\nYou can view any repository in the experimental Blocks interface at [blocks.githubnext.com](https://blocks.githubnext.com). \n\n\u003e 🚨 Blocks is currently a technical preview! It may have bugs or issues, and should not be used for anything mission-critical.\n\nCheck out some [blocks examples](https://blocks.githubnext.com/githubnext/blocks-tutorial) to get a better sense for what you can do with blocks, and see how some are authored.\n\n## Under the hood\n\nA Block is a React component; it receives a fixed set of props and returns JSX. The Blocks web application which knows how to find and render Blocks provides props describing the content to render, and also hooks that a Block can use to update content or metadata, or call the GitHub API.\n\nThere are two kinds of of Blocks: File Blocks and Folder Blocks. Their API is mostly the same, except that File Blocks receive file content and Folder Blocks receive folder content.\n\n## How are blocks developed and shared?\n\nYou develop blocks in regular GitHub repos. You can [click here to use this repository as a template](https://github.com/githubnext/blocks-template/generate) to develop a new block!\n\n\u003e 💖 There's no deploy step needed! Just push your block to a GitHub repo in order to use it, anywhere.\n\nOnce developed, you can _use_ a block on any repository by picking it from the block picker:\n\n![Screenshot showing the blocks interface with the block picker open and the mouse cursor hovering over the Markdown block](https://user-images.githubusercontent.com/22723/184006093-3dc85092-a783-4cf3-a953-0dc786f8bd62.png)\n\nIf your block is not in the dropdown list, you can paste the URL of the repo containing your block into the search field at the top of the block picker.\n\nA block can be used by anyone that can see the repository where that block is developed. If the repo is private, it is usable by any collaborator that has read permissions (or better) on that repo.\n\n## Blocks API\n\nThese are the props that are supplied to every block when it is mounted. These are fixed: you can't change the props, and you can't control the information passed in each prop as they are automatically determined based on the underlying repo and content.\n\n### Props received by all blocks\n\n```ts\ninterface CommonBlockProps {\n  // metadata from `blocks.config.json`\n  block: {\n    id: string;\n    type: string;\n    title: string;\n    description: string;\n    entry: string;\n    matches?: string[];\n  };\n\n  // context about the repo, file / folder, and version\n  context: {\n    path: string;\n    file: string; // for File Blocks\n    folder: string; // for Folder Blocks\n    repo: string;\n    owner: string;\n    sha: string;\n  };\n\n  // arbitrary metadata for Blocks to store configuration etc.\n  metadata: any;\n  onUpdateMetadata: (newMetadata: any) =\u003e void;\n\n  // callback to call any GET endpoint in the GitHub API:\n  // https://docs.github.com/en/rest/overview/endpoints-available-for-github-apps\n  // e.g. `/repos/{owner}/{repo}/contributors`\n  onRequestGitHubData: (\n    path: string,\n    params: Record\u003cstring, any\u003e\n  ) =\u003e Promise\u003cany\u003e;\n\n  onNavigateToPath: (path: string) =\u003e void;\n\n  // returns a list of repos containing blocks\n  onRequestBlocksRepos: (params?: {\n    path?: string; // restrict to blocks that match the path (see `matches` in `blocks.config.json`)\n    searchTerm?: string; // restrict to blocks matching the search term\n    repoUrl?: string; // restrict to blocks in the repo\n    type?: \"file\" | \"folder\"; // restrict to blocks of this type\n  }) =\u003e Promise\u003cBlocksRepo[]\u003e;\n\n  // a React component that renders a block viewing a file or folder\n  BlockComponent: ({\n    block,\n    context,\n  }: {\n    // owner, repo, and id identifying the block\n    block: { owner: string; repo: string; id: string };\n\n    // optional context specifying the file or folder to view;\n    // omitted context or omitted fields default to the current file or folder\n    context?:\n      owner?: string;\n      repo?: string;\n      path?: string;\n      sha?: string;\n    };\n  }) =\u003e JSX.Element;\n}\n```\n\n### Props received by file blocks\n\n```ts\ninterface FileBlockProps {\n  // the file content\n  content: string;\n  // callback to update the file content\n  onUpdateContent: (newContent: string) =\u003e void;\n\n  // the original file content\n  originalContent: string;\n\n  // whether or not the user can edit the content\n  isEditable: boolean;\n}\n```\n\n### Props received by folder blocks\n\n```ts\ninterface FolderBlockProps {\n  // flat list of files and folders in the tree\n  tree: {\n    path?: string;\n    mode?: string;\n    type?: string;\n    sha?: string;\n    size?: number;\n    url?: string;\n  }[];\n}\n```\n\n### Metadata\n\nBlocks can store freeform metadata about themselves in the repository where they are _used_ (as opposed to the repo containing the code that powers the block). Metadata is unique per file/folder per Block, and committed to the [`.github/blocks/`](https://github.com/githubnext/blocks-tutorial/tree/main/.github/blocks) folder.\n\nOften, metadata is used to store configuration data, so that you can customize how a block works by default for a specific file or folder in your repository.\n\nTo store or update metadata, call the `onUpdateMetadata` hook which is supplied to your block as a prop. The user will be prompted to accept the change and commit the new metadata to the repository.\n\nWhen you next view the specific content with that block, the metadata prop will contain the committed metadata.\n\n### Updating content\n\nBlocks are not just for viewing content! They can also offer custom interfaces for editing content.\n\nThere are three props which facilitate editing content in blocks:\n\n- `originalContent`: contains the original contents of the file. This is useful if you want to be able to display a diff of changes made, but not yet committed.\n- `isEditable`: a flag indicating whether the current user has permission to edit the file. If it is false, the block should disable editing functionality, if present.\n- `onUpdateContent`: a hook that can be called to update content programmatically. Users can also commit changes using the `Save` button in the Blocks interface next to the block picker. Whether triggered via the hook or the save button, the Blocks application will display a dialog containing a diff to the user, and ask for their permission to make a commit. Commits recorded as being authored by the user that made (and approved) the request.\n\n\n### Nesting\n\nBlocks can be nested inside other blocks using `BlockComponent`; you can\nretrieve a list of available blocks using `onRequestBlocksRepos`. See the\n[edit-and-preview](https://github.com/githubnext/blocks-examples/blob/main/blocks/file-blocks/edit-and-preview/index.tsx)\nblock for an example of their use.\n\n\n### Caveats and callouts:\n\n- You can import CSS files or split your block into multiple TypeScript files, and everything will be bundled with your block.\n- You can use any third-party dependencies from NPM; just add them with `yarn add` and import them as usual, and they'll be bundled with your block.\n- The [GitHub Primer React components](https://primer.style/react/) are already included as a dependency\n- Your Block entry file **must have the Block component as its default export** so the GitHub Blocks application can find it.\n\n### Relevant repos\n\n[Blocks examples](https://github.com/githubnext/blocks-examples)\n\nExample blocks that we've built to showcase the API.\n\n[Blocks utility library](https://github.com/githubnext/blocks-dev)\n\nThis template project includes a dependency on the GitHub Blocks utility library, which contains types and functions to make it easier to build Blocks. You can import them from `@githubnext/blocks`; see [the repository page](https://github.com/githubnext/blocks-dev) for more detail.\n\n## Setup\n\nThis repo is a template! To use it just click on the \"Use this template\" button on the top right to set it up for your use.\n\n\u003cimg width=\"495\" alt=\"\u0026quot;Use this template\u0026quot; button\" src=\"https://user-images.githubusercontent.com/8978670/144893319-5d45ab5c-12c0-42b4-99f8-97f658deb03b.png\" /\u003e\n\nThe button will take you to a screen to specify what you want to name your own repo.\n\n\u003cimg width=\"801\" alt=\"\u0026quot;Create a new repository from blocks-template\u0026quot; screen\" src=\"https://user-images.githubusercontent.com/8978670/144893351-25b24bfa-3400-4e92-9a2a-618b3ac06a5e.png\" /\u003e\n\n## Step 1. Develop locally\n\n```bash\ngit clone \u003crepo URL\u003e\nyarn # install dependencies\nyarn start # start the dev server\n```\n\nA development server should now be running on [localhost:4000](localhost:4000).\n\n## Step 2. View your development server within the Blocks app\n\nWhen you visit [localhost:4000](localhost:4000) in your browser, you'll be\nredirected to the Blocks app with your locally-developed blocks embedded in it.\nYou can see your blocks by clicking on the block picker toward the top of the\npage (they're shown at the top in blue with a plug icon):\n\n\u003cimg alt=\"Block picker\" src=\"https://user-images.githubusercontent.com/56439/181648955-101b6567-3f9b-44b3-af99-7ef3ca6161b9.png\" width=\"418\" /\u003e\n\nThis starter project has one example folder block and one example file block.\nTry choosing one of the example blocks in the block picker to see it in action.\nWhen you make changes to the block code it will be hot-reloaded in the app.\n\nIf you don't see your blocks, make sure that:\n\n- your development server is running\n- there's a `devServer` query parameter in the URL pointing to your development server [like this](https://blocks.githubnext.com/githubnext/blocks-tutorial?devServer=http://localhost:4000)\n\n## Step 3. Build your custom Blocks with the GitHub Blocks API\n\nTo create your own custom blocks you need to do two things:\n\n### Step 3.1: Define your custom block\n\nIf you open up `/blocks.config.json`, you'll notice an array of block objects\nwith the definitions for each custom block. Block objects follow this interface:\n\n```ts\ninterface BlockDefinition {\n  type: \"file\" | \"folder\";\n  id: string;\n  title: string;\n  description: string;\n  entry: string;\n  matches?: string[];\n}\n```\n\nYou have to define these properties for your own custom Block.\n\nFrom top to bottom:\n\n- `type` determines whether this block applies to files or folders.\n- `id` is an identifier string for this block, like `\"code-block\"`. GitHub Blocks uses this to determine which block to render; it needs to be unique within your project.\n- `title` and `description` control how your block appears within the GitHub Blocks application.\n- `entry` is the path (relative to the project root) to the block's entry point, like `\"blocks/example-file-block/index.tsx\"`.\n- `matches` is an array of globs describing filenames for which GitHub Blocks should show this block as an option, like `[\"*.json\"]` for a block that handles JSON files, or `[\"*\"]` for one that handles any file. (The glob syntax follows https://github.com/micromatch/picomatch)\n\nNote that the `/blocks.config.json` file is not hot-reloaded, so you'll need to\nrefresh the app to pick up changes.\n\n### Step 3.2: Code your Block\n\nYour code goes in the `blocks/` directory. Here you'll find the two example\nblocks as a starting point. You can modify them, rename them (don't forget to\nupdate `blocks.config.json` if you change the path or file names), or just\ndelete them.\n\n## Step 4. Deploy your Blocks to production\n\nTo make your custom block accessible to the Blocks app without your development\nserver running, there are a few steps you need to take:\n\n### Step 4.1: Commit your code and push a new tag\n\nThis template includes a GitHub Action to build a production version of your\nblock. All you need to do to kick off the build process is commit and push your\ncode changes, then create a new tag:\n\n```bash\ngit commit -m'made an awesome block'\ngit push\ngit tag 0.9.0 # Create a new tag with your own version number\ngit push --tags # Push the tag to GitHub\n```\n\nThe action runs `yarn build`, which can be more picky than the development\nserver. You might want to run `yarn build` manually to ensure the action will\nsucceed, or to debug an issue if the action fails.\n\n### Step 4.2: Wait for the build process to finish\n\nLook under Actions for your repo to see that your build has finished. The latest successful build should now be accessible in the GitHub Blocks application.\n\nFrom the repository settings page, make sure that your workflow has **Read and write** permissions or the action will fail with a 403 error.\n\n\u003cimg width=\"805\" alt=\"\u0026quot;Workflow permissions\u0026quot; setting\" src=\"https://user-images.githubusercontent.com/5148596/167847856-22ad190a-d73c-4b97-a0e2-c3c854db0d4f.png\" /\u003e\n\n\u003cimg width=\"1097\" alt=\"Successful build action screen\" src=\"https://user-images.githubusercontent.com/8978670/144665796-cb1ff450-c872-47c5-90b3-f74aea10286b.png\" /\u003e\n\n\u003cimg width=\"152\" alt=\"Tagged release\" src=\"https://user-images.githubusercontent.com/8978670/144665673-431e28f9-9e9d-43b3-87f8-1e5d98bed92c.png\" /\u003e\n\n### Step 4.3: Test your production block in the Blocks app\n\nOnce the action has completed, visit [https://blocks.githubnext.com/] (with no\n`devServer` parameter in the URL), then paste your repo URL\n(`https://github.com/{owner}/{repo}`) into the block picker search bar. This\nwill load the production version of your block, without using your development\nserver.\n\nEverything should work the same as before!\n\n### Step 4.4: Add the topic `github-blocks` to your repo (optional)\n\nIf you want your blocks to show up in the block picker in GitHub Blocks, you need to tag this repository with the topic `github-blocks` so the application can find it.\n\n\u003cimg width=\"323\" alt=\"Repository tagged with \u0026quot;github-blocks\u0026quot;\" src=\"https://user-images.githubusercontent.com/8978670/144665902-63543c98-2486-4e13-9c54-f1d4bc6544a4.png\" /\u003e\n\n**This step is optional!** If you aren't ready to share your block with others,\ndon't tag the repo. Your blocks won't show up in the block picker by default,\nbut you can still paste the repo URL (`https://github.com/{owner}/{repo}`) into\nthe search box at the top of the block picker to search blocks in the repo. If\nyour repo is private, only people with access to the repo can see and use your\nblocks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgordonsmith%2Fobservablehq-blocks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgordonsmith%2Fobservablehq-blocks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgordonsmith%2Fobservablehq-blocks/lists"}