GitHub Blocks is an exploration of the question:
What if developers could customize GitHub, not only to present their code and data, but to make it a living, interactive platform?
Instead of feeling like a storage locker for code, can we bring that content to life? What if a designer could view styles from a CSS file instead of having to wait for a developer to transform it? Or if journalists could visualize data right on GitHub, along with how it's changed over time? There are so many possibilities!
This sounds like a lot of work to build, right? With such an enthusiastic & active community, GitHub could build a flexible API that allows users to take control, and help with building out any use case.
In our exploration we've created a first version of this API that let's anyone create their own custom Blocks.
🚀🚀🚀 Check out the Demo: github-blocks.vercel.app
📣 Read this if you're a GitHub user interested in building your own custom Blocks!
A Block is a React component that receives a special set of props and returns JSX. We've implemented two type of Blocks: File Blocks and Folder Blocks. Their API is largely the same, receiving the following props:
interface BlockProps {
block: {
id: string;
type: string;
title: string;
description: string;
entry: string;
extensions?: string[];
};
context: {
path: string;
file: string;
repo: string;
owner: string;
sha: string;
};
metadata: any;
// callback functions
onUpdateMetadata: (
newMetadata: any,
path: string,
block: Block,
currentMetadata: any
) => void;
onRequestUpdateContent: (newContent: string) => void;
onRequestGitHubData: (type: string, config: any, id: string) => Promise<any>;
onNavigateToPath: (path: string) => void;
// if a File Block
content: string;
// if a Folder Block
tree: {
path?: string;
mode?: string;
type?: string;
sha?: string;
size?: number;
url?: string;
}[];
}For simple use cases, the content (the content of the file) or tree (a list of the contained files & folders) prop will be the most useful, with info about the file or folder the user is looking at on the GitHub Blocks UI. But if you need additional context (such as the path to the file or the owner/repo in which the file lives), you can access it via the handy context prop.
metadata is a free-form prop that can be used to store arbitrary data about the file. It's up to you to decide what you want to store in this object: anywhere from definitions of data visualizations in a charts Block to annotations for a code Block. This is unique per file/folder per Block and stored within a .github/blocks/file/ folder within the viewed repo. To update the metadata, you can call the onUpdateMetadata prop with the updated data, which creates a new commit on the repo.
A few caveats and callouts:
- Blocks have access to GitHub Primer CSS styles
- You can use both third-party and relative imports in your Block code! Simply put, feel free to install any dependencies from NPM, or import a local JS/CSS file and it should be included in the final bundle.
- Your Block entry file must have the Block component as its default export. If it does not, bad things will happen.
To reduce the cognitive load associated with writing file and folder Block components, we've assembled a helper library called @githunext/utils that exposes interface definitions and a few helper functions. This list will undoubtedly change over time, so be sure to check out the repository page for more detail.
Example blocks that we've built to showcase the API.
Blocks template starter project
A template for creating your own custom Blocks.
To reduce the cognitive load associated with writing file and folder Block components, we've assembled a helper library called @githunext/utils that exposes interface definitions and a few helper functions. This list will undoubtedly change over time, so be sure to check out the repository page for more detail.
📣 Read this if you're part of the team creating the GitHub Blocks projects.
This repo contains the source code for a prototype app that demos the GitHub Blocks project.
To start working:
yarn # install the deps
yarn dev # start up the serverIn the browser, you can choose the file on GitHub to view with url params:
owner # the GitHub org
repo # the GitHub repo
path # the path to the fileThis app uses GitHub OAuth to authenticate users, with help from the NextAuth package. You'll need to create an OAuth app and provide the required environment variables (app secret and app ID) to the .env.local file.
GITHUB_SECRET=X
GITHUB_ID=X
Just push to main and Vercel will handle the deployment.
To keep loading snappy, our example blocks are pulled in via a preinstall & postinstall script. To update the version of the examples that are pulled in:
- Hop on to the githubnext/blocks-examples repo and push a new tag
git tag X.X.X
git push origin --tags
- Wait for the action in that repo to end and generate a new release
- Come back to this repo and update the version of the
githubnext/blocks-examplelibary inpackage.json
yarn add @githubnext/blocks-examples@https://github.com/githubnext/blocks-examples.git#X.X.X
That's it! Upon building, the example blocks are synced to the /blocks folder (which is completely auto-generated), along with an index.js file that exports the blocks and an index.css file that exports the styles.
Note: this is why we have a list of optionalDependencies: to separate the example Blocks dependencies in order to refresh the list and not cache old deps.
Note: the postinstall script also copies the Excalidraw assets to the /public/excalidraw folder to prevent from pulling then in from the unpkg CDN.