This is the repository for blog.ethereum.org, which includes the codebase for the website as well as all blog post entries. A blog editor (currently Joseph Schweitzer) is required to approve draft posts and merge them into the master
branch to be published on the live site. Once PRs are merged into master
, changes are automatically deployed to the site via our Netlify integration.
If these instructions are confusing to you or you’d rather just submit your blog directly to a blog editor instead of this repository, please reach out to the current editors.
Posts must to be written in Markdown format. If you are unfamiliar with Markdown, see the Useful Markdown Links section at the bottom.
You have the ability to embed images, YouTube videos, Twitter posts, code snippets, and GitHub gists into your blogs.
This is an optional, opt-in feature, that allows you to add a Table of Contents (ToC) at the top of a post. Take into account that ToCs are generated using h2 subheadings and lower, so you’ll need at least one of those in the post content.
You can enable the ToC for a specific post, by adding the optional toc: true
prop on the frontmatter data, e.g.:
---
title: 'A post about the future of Ethereum.org'
date: 2023-08-31
author: Ethereum.org Team
category: Ethereum.org
image: https://storage.googleapis.com/ethereum-hackmd/upload_fc8f505659849ac7e21c75d47f833bbe.png
toc: true
---
In case you don’t want to add the ToC, just skip adding this line.
NOTE: You also have to add the line
toc: true
on the translated markdown files, as this is an opt-in, per file feature.
/public/images/posts
and commit these as part of your pull request (PR)./images/posts/image-file-name
(without /public
)
If this step if confusing you can just send a blog editor the pictures and let them know where you want them in the blog, or paste the images in the message of your PR on GitHub.
Paste the following into your blog entry, replacing T5RcjYPTG9g with the video ID found in the video URL.
<iframe
width="100%"
height="315"
src="https://www.youtube.com/embed/T5RcjYPTG9g"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
Navigate to the Twitter post you want to embed. Click on the arrow in the top-right corner of the tweet and click Embed Tweet
. Copy the code and paste it into your blog.
You can highlight a code snippet by wrapping code segments in triple-backticks (```). Place these backticks in their own lines.
```
def foo
puts 'foo'
end
```
The site uses KaTeX to render LaTeX/math expressions. LaTeX-rendering libs are not 100% compatible with LaTex yet, so please check the support table if you are having issues with some expression.
You can create a post with a date in the future (remember the date in your filename and frontmatter data should match) that will not be included in the build and will remain hidden up to this defined date. This is useful if you want to ‘schedule’ posts to save time in advance.
e.g., a post with the following date:
---
title: 'A post about the future of Ethereum.org'
date: 2023-08-31
author: Ethereum.org Team
category: Ethereum.org
image: https://storage.googleapis.com/ethereum-hackmd/upload_fc8f505659849ac7e21c75d47f833bbe.png
---
will not appear on the blog until 2023-08-31
.
The blog has syntax highlighting support for
bash
javascript
json
python
typescript
solidity
To keep the build size light, we are supporting these languages only for now as they were used previously on the blog. More languages can be added in the future if required.
To make use of this feature, we need to specify the language on the snippet (using markdown syntax), e.g. with a typescript snippet, add typescript
next to the opening backticks:
```typescript
import VM from '@ethereumjs/vm';
import Common from '@ethereumjs/common';
const common = new Common({ chain: 'mainnet', hardfork: 'spuriousDragon' });
const vm = new VM({ common });
Otherwise, it will use bash
as default.
You don’t need to add any extra tag to center images from the post content, just add the image tag using Markdown syntax: 
.
To reference other blog posts in the content, you don’t need to use the full URL. You should just use the relative URL, e.g.:
https://blog.ethereum.org/2022/08/24/mainnet-merge-announcement
should be written as simply /2022/08/24/mainnet-merge-announcement
, without the https://blog.ethereum.org prefix.
Previously, search was available in English only, for English content. Now, depending on your selected language, you can look for posts using the search page in different languages, e.g.: use Spanish if you’re on the Spanish version of the search page.
To avoid any issue and follow a certain standard, filename should be in the following format: YYYY-MM-DD-title-here.md
, e.g. 2015-07-30-awesome-new-blog-post.md
Special characters such as hash signs, ampersands, or question marks should be removed from the title, and it should be kept in “kebab” casing (all lowercase, with words separated by hyphens). Avoid using capital letters.
Please contact an editor listed above before starting if you would like to list your blog post.
Create a .md
document in the src/posts/en
folder with a naming convention and frontmatter content (title, subtitle, date, author, image, category) similar to blog entries in the src/posts/en
folder (see examples below).
Translation note: Original English posts should be placed in the above folder (/src/posts/en
). Translated versions of this post will be placed in their corresponding language folder, ie: /src/posts/es/
for Spanish. See below for more information regarding internationalization.
You can create a new file by navigating to the src/posts/en
folder above and clicking Add file
then Create new file
.
Remember that filename should follow the naming convention.
The beginning of the post should start with metadata about the post in the following format:
---
title: 'Awesome new blog post!'
subtitle: 'Subtitle or description (optional line)'
date: 2015-07-30
author: Your Name
category: Category Name
subcategory: Optional Subcategory Name
image: /images/posts/2015/07/my-banner.png
---
The title
here will be used as the main header (h1
) of the page. Avoid using any other h1
headers in the post (a line starting with a single #
in markdown) and start using h2
(##
in markdown) directly in the content when needed.
The subtitle
field is optional. If you chose not to include one, remove this entire line.
The date
field must match the date being used in the filename.
The category
field must match one of the existing category names (otherwise the building process will fail):
The subcategory
field is optional and does not appear anywhere on posts. It is only visible in the RSS feed. Currently this field is only used to differentiate Devcon posts from Devconnect posts under the “Events” category. When making a post with the “Events” category, please specify one of the following subcategories:
The image
field should be the path to the image (/images/posts/[image-name]
) you want to use as the banner for the post. If you have an external link to an image, you can use that here. If you have an image you’d like to upload, you can either include this as a commit to your pull request (PR), or simply paste it in the message of the PR on GitHub and request assistance. The image
is not a required, but encouraged. In case you don’t want to provide an image path, remove this line and a default image will be used for the post header section.
Images from the post content are centered automatically, so please don’t include any <p>
tag to center them and reduce the clutter.
NOTE:
layout
andpublished
fields are not used anymore, you don’t have to include them.
The rest of your post can be entered beneath this section.
Additionally please look at other blog posts in the src/posts/en
folder to make sure you are doing it correctly.
Once your post from above is ready, you can submit a pull request (PR) to the master
branch at the bottom of the edit page on GitHub.
Next, contact a blog editor (above) who will look over the post to make sure it is formatted correctly. Once it is approved it will be merged into the master
branch where it will be broadcast live to the interwebz. Only blog editors can push blog entries to that branch. It can take up to ten minutes for the blog post to propagate and display on blog.ethereum.org. If you let Joseph Schweitzer know ahead of time we can post a link to your blog on the official @ethereum Twitter, LinkedIn, Facebook, and /r/ethereum subreddit.
As the blog now supports internationalization, authors can order translations for new and upcoming blog posts, and suggest older posts that should be translated.
If you are in the process of drafting a blog post or have recently published one, we can order translations from Acolad, our language service provider.
All new English posts should be placed in the src/posts/en
folder, where they can subsequently be sent for translation upon request. For reference, note that all of these translated files will maintain the same filename as the original English post, but will be placed in a folder with the language code (e.g. src/posts/zh
for Chinese).
The majority of posts on the EF blog are most popular in the few days following their publication and become less relevant and accurate as time goes by. Therefore, publishing translations as soon as possible ensures that the majority of visitors can read them in their native language and allows for maximum accessibility. The best way to achieve this is using Acolad, as we can set deadlines and expedite certain translations.
In case you want to request translations for a new or upcoming blog post, you can fill out this form and the translation program team will process the order as soon as possible.
In addition to translating new posts, we upload some older posts that are still relevant, popular, or have some historical significance, to Crowdin (the localization tool that we use) and allow our community of translators to translate them at their own pace.
If you have written a blog post in the past and would like to get it translated, please fill out this form and the translation program team will process the order as soon as possible.
Since these posts will be translated by the community, we do not have much influence on the timelines for translation. Community-translated posts will be updated on the blog periodically.
In case you receive questions from people wanting to translate a blog post or get involved with the translation process, please direct them to our EF blog translation guide.
If you’re looking to update a post that is NOT translated, simply open a PR with your suggested changes.
Updating translated posts is not as straightforward, so there is an additional step in the update process:
If you are only updating links, images, or numbers (e.g. version numbers) in a post, feel free to change this content in all languages the post has been translated into in the same PR.
In case of changes to the source text, please let Luka know by tagging @lukassim in the PR on GitHub, sending a message with a link to the PR in the #website-blog
channel on the EF Discord, or sending an email to translations@ethereum.org.
Based on the importance, urgency, and scope of the changes, we will either order translations for the updated content from Acolad, or ask the community for help. We may also choose to translate content using machine translations as a temporary solution until human translations are completed.
The main stack used in the project includes:
react-intl
for i18n support.feed
for RSS support.next-sitemap
to generate sitemap.This is a Next.js project bootstrapped with create-next-app
.
First, clone the repository:
git clone https://github.com/ethereum/blog.git
cd blog
yarn
Install dependencies:
yarn
Run the development server:
yarn dev
Open http://localhost:3000 with your browser to see the result.
react-intl
, TypeScript & eslint
setupWhen using react-intl
together with TypeScript, id
prop is mandatory for FormattedMessage
component to avoid compiler issues on strict mode. A way to fix this is setting up an eslint
rule to trigger an error, which you can find on .eslintrc.json
to enforce the id generation based on the string content. Then you’ll be able to generate the proper id by using the TypeScript quick-fix tools.
"rules": {
"formatjs/enforce-id": [
"error",
{
"idInterpolationPattern": "[sha512:contenthash:base64:6]"
}
]
}
To learn more about Next.js, take a look at the following resources:
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
We also use Chakra-UI for styling. Check out the Chakra-UI documentation to learn more.
If you have any feedback on the blog, localization process, or process for publishing posts, please open an issue or let us know on the #website-blog
Discord channel or by sending an email to website@ethereum.org.
We would also like to hear any ideas about improvements or additional functionality that could be added to the blog with future updates.