We love your input! We want to make contributing to the Solana Developer content as easy and transparent as possible, whether it's:
- Reporting a bug
- Submitting a fix
- Discussing the current state of this repo
- Proposing new features
- called the "developer content" repo or "developer content API"
- written in markdown with YAML frontmatter
- uses YAML frontmatter, with structure enforced with Contentlayer
- all pages are grouped into "content types"
- supports some select custom components
- publicly displayed via the UI of solana.com (located in a different repo)
- content translations are supported via Crowdin
- code blocks must use code-import for file snippets (via filesystem)
- code file should be tests and should add code ranges instead of whole test file
To aid in keeping both consistent content and a high-quality experience, all code/content maintained within this repo shall use the style guidelines set forth here.
Failure to adhere to these style guidelines will slow down the review process and block approval/merging.
The guidelines below are consistent with Solana Foundation Style Guide, and TERMINOLOGY, to ensure consistency with other content on solana.com. There are also a few additional items aimed at technical documents.
In particular:
- Please run a grammar check (not just a spell check) before creating a PR -
this will catch many of the small errors that occur from your editing process,
and save the person reviewing your PR some time. We recommend
Grammarly. In
your Grammarly dictionary, you may
wish to add Solana-specific words like
lamport
,blockhash
, etc. - Use US English rather than British English. Grammarly will catch this for you.
- Use 'onchain' (not on-chain, definitely not smart contract) when referring to onchain apps. This comes from the Solana Foundation style guide, and is intended to be similar to 'online'.
- Use sentence case for headlines (”Solana Foundation announces new initiative” instead of “Solana Foundation Announces New Initiative”). This also comes from the Solana Foundation style guide.
- Use the terms 'blockchain' or 'web3' rather than 'crypto'.
- Use 'SOL' rather than 'Sol' to refer to Solana's native token. Don't call the native token 'Solana'!
- Use 'secret key' rather than 'private key' to be consistent with web3.js. Note: this will change in a future version of web3.js, but right now the ecosystem uses 'secret key'.
- Use 'wallet app' for software. 'wallet' for the address that holds value, to avoid confusion between these two concepts.
- PDAs are not public keys. It is not possible to have a public key without a secret key. A public key is derived from a secret key, and it is not possible to generate a public key without first generating a secret key.
- Be careful about the term 'token account'. A
'token account' is any account formatted to hold tokens,
and being specific (rather than, for example, swapping between 'associated
token account' and 'token account') makes this clearer.
- Use the specific term 'associated token account' rather than just 'token account' if you're referring to an account at an associated token address.
- Use 'token mint account' to refer to the address where a token is minted. E.g., the USDC mainnet token mint account. Use apostrophes of possession, including for inanimate objects. Eg 'the account's balance' is correct.
- Don't use 'here' links. They make the course hard to scan and 'here' links are bad for SEO.
- JS/TS clients send
transactions
made frominstructions
. Onchain programs haveinstruction handlers
that processinstructions
. Do not refer to instruction handlers as instructions! The reason is simple: an instruction cannot process an instruction. Themultiple
template in Anchor also calls these functionshandler
s.
You're writing code to be read, understood, and changed by others.
We want the minimal amount of code necessary to solve the problem.
- Use full names. Call a
thing
athing
. Don't call it athg
- this means we don't have to say 'thing, spelled tee aitch gee' every time we talk about it, and means developers (who often don't speak English as a first language) have to work out that 'thg' means 'thing'. - Avoid repetitive, copy-paste code. This helps others change the code easily, as they can update and fix things in a single place. If there's some boilerplate, Solana-specific code you always need make a PR to the 'helpers' repository.
- Avoid magic numbers. Nobody should see a
+ 32
in your code and wonder what the32
means. See Anchor notes below for Anchor-specific advice.
We're trying to focus on Solana, not teaching JS/TS development and setup. This means reducing the JS/TS concepts needed to understand our demo code.
-
On the command line (not in the browser),
.ts
files are run on the command line withesrun
, which supports top-levelawait
, doesn't require atsconfig.json
, etc. There is no need forasync function main()
wrappers or IIFEs.await
just works. If you see these wrappers, remove them. -
Likewise, use
async
/await
and consistently usetry
/catch
all the time, rather than sometimes using.then()
and.catch()
-
Throw errors with
throw new Error('message')
. Don't throw strings (JS allows almost any type to be thrown). TS code can assume anything thrown is of theError
type. -
Don't make custom helper functions. Instead, use the
@solana-developers/helpers
package. If you need a function that doesn't exist, make a PR to@solana-developers/helpers
, and add the helper, tests, and docs. -
Write tests so we can run your code and ensure a new release of something doesn't break your code. Ensure your tests make sense. BDD style (which Anchor uses by default) uses
describe
andit
to create the names. Sodescribe('the plus operator', () => {}
becomesdescribe the plus operation
when the tests run, andit('adds numbers', () => {...})
becomesit adds numbers
when the tests run. Ensure your test names make sense! -
Use prettier to help maintain the quality and consistency of the content. There is a master prettier configuration file (
.prettierrc
) at the root of this repo. Note that whileprettier
can format Markdown, prettier doesn't yet support language-specific settings inside Markdown files so you'll need to format the code yourself for now. To do this quickly, make a blankdeleteme.ts
file, paste your code in there, and then save it (allowing prettier to do its work), and paste it back into the document.
On all commits and PRs, there is a GitHub action that checks if your PR follows the configured prettier formatting. If your branch/code does NOT meet the prettier formatting requirements, it will not be merged and it will delay its review.
If your editor is not configured to auto-format on save using prettier, then you can run the following command to auto-format all files in your local repo/PR:
pnpm prettier:fix
You can also run the prettier check command to see which files do not follow the prettier formatting guidelines.
pnpm prettier
-
Avoid magic numbers. People reading your code should be able to understand where values come from. Use InitSpace to calculate space needed for accounts, and add a constant
DISCRIMINATOR_SIZE
toconstants.rs
.-
Bad:
8 + 32 + 32 + 8 + 8
-
Good:
space = DISCRIMINATOR_SIZE + SomeAccount::INIT_SPACE
-
-
Use
rustfmt
. -
Use
UncheckedAccount
rather thanAccountInfo
.UncheckedAccount
is a much better name, and makes it explicit that the account is not being checked by Anchor. -
Don't manually ask people to edit
declare_id!()
andAnchor.toml
. Instead, ask them to runanchor keys sync
. -
Use the multiple files template to organize very large Anchor projects.
The content within a document should not start with a heading. It should start with a paragraph of text. After each heading should be non-heading content (i.e. do not stack headings without putting a sentence/paragraph between them).
Content should NOT include a h1
tag within it since solana.com will
automatically render a specific h1 based on the frontmatter's title
. As such,
all markdown h1
will be auto-converted to a h2
.
The text within a heading should NOT include inline code blocks (i.e. single
backtick). They should also NOT end with any special characters (i.e.
:;!@#$%^&*
). Ending in a question mark is allowed.
All h2-h4 headings will be auto-anchored, allowing anyone to directly link to the specific section of content. A clickable anchor link will be rendered aside from the heading's text.
## Simple, valid heading
The heading above is valid! Yay :)
## This is an `invalid` header
The heading above is invalid since it contains single backticks, aka this
character: `
### This is also invalid:
The heading above is invalid since it ends with a colon.
# this will become an h2
The heading above will be converted to a `h2` element
## This is okay?
The heading above is valid! Yay :)
#### this in not okay since it does not increment by only 1
The heading above is invalid since it skips the `h3` heading (`###`)
When linking to other developer content pages (i.e. docs, guides, etc) or images, the markdown link should use the absolute path to the file relative to the repo root. Include the file extension.
For example: /docs/index.md
(good) vs https://solana.com/docs
(bad)
By linking to files this way, code editors will get better auto-complete for files and subheadings within those files. Ultimately providing a better experience when editing/maintaining the content.
It will also allow GitHub to provide correct links to view the markdown files within GitHub itself.
Using relative paths and directory climbing is not allowed. Including "single
dot" and "double dot" (i.e. ./file.md
and ../../another.md
).
This is a good link to the [Accounts](/docs/core/accounts.md) document
This [linking without](/docs/core/accounts) the `.md` file extension should be
avoided.
This is a good link to the
[Instructions](/docs/core/transactions.md#instruction) section of the
Transaction document.
This is a another good link to the
[Hello World in your browser](/content/guides/getstarted/hello-world-in-your-browser.md)
document.
For images, you can use the path starting with `/public` like this:
![this is the image caption](/public/assets/guides/hello-world/solpg.gif)
Note: When the content pages are generated and deployed on solana.com, the links will be automatically adjusted to function on the website. Including making the images viewable and removing
.md
file extensions.
In addition to standard markdown "fenced" code blocks (i.e. using triple backticks), the developer content repo requires the use of code-import for file snippets. This ensures that code examples are always up-to-date with the actual source files.
To use code-import, follow these steps:
Ensure your code file is a test file located in the appropriate directory within the repo. Use the following syntax to import code snippets:
This will import lines 1-10 and 15-20 from the specified file.
Always use code ranges instead of importing whole files. This helps keep examples concise and focused.
- The file path must start with a forward slash (/).
- You can specify multiple line ranges, separated by commas.
- Line ranges should be in ascending order and not overlap.
- Invalid ranges (e.g., #L4-L3) are not allowed.
- Line numbers start at 1, so #L0 is invalid.
- Trailing commas in the range specification are not allowed.
Example of a valid code-import:
Example of an invalid code-import:
This is invalid because the ranges are not in ascending order and overlap.
When a content page is rendered on solana.com, a table of contents will be
auto-generated based on the markdown headings within the document's content.
Specifically: all h2
, h3
, and h4
(aka h2-h4) tags will be included in the
table of contents. All headings greater than h4
will be ignored from the table
of contents.
The exact text within the heading will be rendered as a line item in the table of contents. As such, thought should be put into the text within a heading, especially for accessibility.
Since the content within this repo is meant to be publicly displayed on
solana.com
, we have very specific guidelines for the content that can be
merged into this repo (and eventually displayed on solana.com).
If you would like to submit changes to existing content or add new content, all of these guidelines should be observed:
Avoid any language around making "official recommendations" such as "I recommend product X" or "Product Y is the best". The content within this repo will be publicly published on solana.com which is maintained by the Solana Foundation. As such, any "product recommendations" may appear as if coming directly from the Solana Foundation. The Solana Foundation does not make official recommendations for products but rather helps share what options are available in the broader Solana ecosystem.
Avoid language similar to "service X is my favorite". When talking about or naming specific products within the Solana ecosystem, writers and maintainers should make their best attempt to list multiple options that meet similar use cases. As a general rule of thumb, try to share at least 3-4 options of similar products or services, not just a single named one. For example, when talking about wallet providers, a writer could list Phantom, Solflare, Backpack, and Ultimate. When talking about RPC providers, a writer should link to solana.com/rpc instead of listing specific names.
Write content that uses up-to-date code. Code bases change, functions get deprecated, and methods get removed. When submitting code snippets within the content here, use the most up-to-date code available for the given functionality being used. Especially net new content, like adding a new guide.
This repo contains multiple different types of developer content records. Each
grouping of records (called a "content type") serves a different purpose and is
handled differently when displayed in the UI of solana.com
.
Below is a table describing each active content type group, including the corresponding path within this repo and webpage for viewing on solana.com:
Content Type | Repo Path | Webpage URL |
---|---|---|
docs |
/docs |
View Docs |
rpc |
/docs/rpc |
View Docs |
courses |
/content/courses |
View Courses |
guides |
/content/guides |
View Guides |
resources |
/content/resources |
View Resources |
cookbook |
/content/cookbook |
View Cookbook |
Note: Even though
rpc
is technically a different record type, it is treated effectively the same as thedocs
type. More details in the "Frontmatter for Docs" section.
Every piece of content within this repo is normally written in markdown with YAML frontmatter. With some support for specific custom components using React and MDX.
We typically use GitHub flavor markdown (GFM) which you can learn more about here.
The YAML frontmatter is used to provide additional metadata about a given piece of content. Each content type has specifically supported frontmatter fields, some required and some optional. All available frontmatter fields are enforced via Contentlayer (see more below).
Here is an example of the frontmatter within a piece of content within this repo.
Contentlayer offers YAML frontmatter structure enforcement and type safety for the frontmatter fields on every content record, including generating TypeScript types for each content type's grouping.
Contentlayer does this code generation by enabling us to define a custom data
schema for the frontmatter. You can view the current Contentlayer schema here:
contentlayer.config.ts
If any content records contain unsupported or misspelled fields in the frontmatter, Contentlayer will throw an error, preventing misconfigured content from being shipped to production.
Each content type (i.e. docs
, guides
, resources
, etc) has the ability to
support different custom metadata fields within the YAML frontmatter.
While each content group may support additional frontmatter fields, the following are default supported by all content groups:
The primary title of the individual piece of content
- name:
title
- type:
string
- required:
true
Example: this line of text
The title
field will be used in auto-generating the social share card images
(aka OpenGraph images) that people will see when linking to the content page on
solana.com.
Brief description of the content (also used in the SEO metadata)
- name:
description
- type:
string
- required:
false
Example: these lines of text
This field will be used when a short description is needed to be displayed, like in typical blog style "overview cards" and for SEO related metadata.
List of filterable tags for content
- name:
tags
- type:
string
- required:
false
Example: these lines of text
List of keywords for the content, primarily used for SEO metadata when generating the website.
- name:
keywords
- type:
list
ofstring
s - required:
false
Example: these lines of text
The date this content was published
- name:
date
- type:
date
as an ISO-8601 formatted date (i.e.2023-12-06T00:00:00Z
) - required:
false
The date this content was last updated
- name:
updatedDate
- type:
date
string in the format ofMMM DD, YYYY
- required:
false
At this time, the docs
content type does not have any additional frontmatter
fields. They only support the shared default fields
Note: While technically, the
docs
content type is separate than therpc
content type, these are effectively treated the same. Therefore, assume all mentions ofdocs
also apply to therpc
type unless otherwise noted.
At this time, the guides
content type does not have any additional frontmatter
fields. They only support the shared default fields.
In addition to the default frontmatter fields, resources
support the following
fields:
General category of the resource
- type:
enum
- required:
true
- values:
documentation
framework
sdk
Repository URL for the developer resources
- type:
string
- required:
false
In addition to the standard GitHub-flavored markdown text, the content records within this repo support some extra functionality powered by custom React components (or custom implementations of standard components). The following is a list of available components
Note: While markdown does normally support standard HTML tags being rendered with the rest of the markdown document's content, this should normally be avoided within this developer content repo. See more in the Style Guidelines section.
- html tags - generally not to be used, with a few exceptions
- headings - details about how to include headings in a piece of content
- images - details about how to include images in a piece of content
- code blocks - additional functionality on top of standard markdown code blocks, these support code file import from filesystem
- blockquote - additional functionality on top of the standard
HTML
blockquote
element - Callout - custom component used to render message to the reader in a more visually distinctive way (i.e. error messages, warnings, etc)
- Embed - embedding specific types of external content (i.e. YouTube videos)
Since the content files in this repo use markdown, using HTML tags within should be avoided. The markdown content render/parser will handle converting the text blocks into the associated HTML code blocks when rendered on page.
A few HTML elements are acceptable to use, the rest will normally be rejected:
details
- see this example
Headings (i.e. standard HTML tags of h1, h2, h3, etc) can be added to content using standard markdown-based headings.
Example:
# this gives an h1 (and should not be used)
## this gives an h2
### this gives an h3
#### this gives an h4
##### this gives an h5
Note: All markdown
h1
s will be auto converted to anh2
tag.
See more details above about the Heading styles.
Images can be embedded directly within markdown using standard markdown
formatting. Each image should use the absolute path of the image file located
within this repo (i.e. /public/assets/*
) and have a descriptive label
attached. This label will be rendered on the page with the image.
All images should be uploaded via a PR to this repo and stored within the
/public/assets/
directory.
Embedding external images within markdown is generally not allowed.
![descriptive label here](/public/assets/docs/transaction.svg)
In addition to standard markdown "fenced" code blocks (i.e. using triple backticks), the developer content repo supports additional functionality on top of code blocks.
Code block syntax highlighting and some other features utilize
rehype-pretty-code
andskiki
under the hood for some of the additional functionality supported.
Fenced code blocks support including additional metadata within their first line, called "meta strings". These meta strings allow the markdown processor to handle different logic of this user-defined metadata.
While most are familiar with language meta string, which enables syntax
highlighting. The following example has the language meta string of
typescript
:
```typescript
const example: string = "'typescript' is the language meta string";
```
This repo supports other meta strings as well:
- the standard syntax for the language value, which enables syntax highlighting
- file names
- line highlighting
- character/word highlighting
- diff lines
Each code block can have syntax highlighting for its specific language by noting the language used immediately following the triple backticks.
Most code languages are supported including these commonly used ones: rust
,
ts
or typescript
, js
or javascript
, toml
, shell
The code block's language should be in all lowercase and have no space between the language name and the backticks.
Examples:
```ts
const example: string = "this is typescript";
```
```shell
solana airdrop 2
```
Code blocks can have an optional header displayed on top of the code block element itself. This is commonly used to display the "file name" in which a code snippet comes from.
To display the file name header on a code block, in the same line of the triple
backticks and language add the filename=
field and set your desired name.
Examples:
```ts filename="index.ts"
const example: string = "this is typescript";
```
```rust filename="lib.rs"
use anchor_lang::prelude::*;
declare_id!("Bims5KmWhFne1m1UT4bfSknBEoECeYfztoKrsR2jTnrA");
```
Code blocks can highlight lines using the
syntax provided via rehype-pretty-code
.
In the meta string, place a numeric range inside {}
.
Examples:
```js {1}
// this will be highlighted
// this will NOT
```
```rust filename="lib.rs" {1,3}
// this will be highlighted
// this will NOT
// this will be highlighted
```
Code blocks can highlight characters using the
syntax provided via rehype-pretty-code
.
In the meta string, place character segments between two /
symbols. You can
also highlight multiple segments of characters.
Examples:
```js /word/
// this will be highlighted: word
// this will partially highlight: hello-word
```
```js /word/ /partially/
// this will be highlighted: word
// this will partially highlight: hello-word
```
```rust filename="lib.rs" /word/ {1,3}
// this will be highlighted: word
// nothing highlighted
// this will partially highlighted word and line: hello-word
```
Within a code block, you can create a "diff" style line (i.e. a line was added or a line was removed) using the
At the end of a line, add one of the following notations to the end of a line:
- to show a red "line removed" diff line:
// [!code --]
- to show a green "line added" diff line:
// [!code ++]
Example:
const keep = "line that stays";
const updated = "example that was should be removed"; // [!code --]
const updated = "example that was should be added"; // [!code ++]
const didNotChange = "this line did not change";
Standard markdown quote blocks are rendered with our custom
Callout
component.
Nested blockquotes are not allowed.
> This will be the `Callout` component with its default styles
Multiline blockquotes are allowed and will still be rendered as the default
Callout
.
> This blockquote will also be rendered as the `Callout` component with default
> styles. But since the text is much longer (and we enforce a Prettier
> configuration with a max line width) it should be wrapped to multiple lines.
> > This is a nested blockquote and is not allowed
The custom Callout
component can be used to render a message to the reader in
a more visually distinctive way (i.e. error messages, warnings, etc). Allowing a
writer to draw more focus to a specific statement.
Note: All standard markdown blockquotes will be auto converted to a
Callout
component with default styles.
The contents within the Callout
(aka children
in React land) will be
processed just like any other markdown. So you can put code blocks, lists, or
whatever else inside.
<Callout>
This will be the default "info" type callout
</Callout>
The Callout
component has the following props:
- name:
type
- required:
false
- type:
enum
- default:
info
- values:
info
- a blue callout block (equivalent values:note
,blue
)warning
- a red callout block (equivalent values:yellow
)caution
- a yellow callout block (equivalent values:yellow
)success
- a green callout block (equivalent values:green
)
- required:
- name:
title
- override the default title that is thetype
- required:
false
- type:
string
- default: the same text as the
type
's enum variant
- required:
<Callout type="caution">
this will render as a yellow callout with a title of "Caution"
</Callout>
<Callout type="success" title="You did it!">
this will render as a green callout with a title of "You did it!"
</Callout>
Callouts can also render other markdown content within it, just like you might expect. See this example here or the one below:
<Callout type="success" title="You did it!">
- list item 1
- list item 2
</Callout>
The custom Embed
component can be used to handle custom rendering of
specifically supported external content.
Currently supported external content:
To embed a YouTube video:
<Embed url="https://youtu.be/eudSSvyZF9o" />
Or
<Embed url="https://www.youtube.com/watch?v=eudSSvyZF9o" />
To embed a Whimsical diagram:
<Embed url="https://whimsical.com/embed/PjBVAREV3DMyW5722tFKJ8" />
Note: The preferred way to display a Whimsical diagram is to export the diagram as an SVG and directly embed the image within a specific piece of content. You can get an SVG export from Whimsical by appending
/svg
to the end of a Whimsical URL.
- If you draw Solana elliptic curves, these are Edwards curves. This is what the curve Ed25519 uses looks like. Solana public keys are the Y values on this curve (we can omit the X value because the curve is symmatrical). Ed25519 is symmetrical, and looks like a slightly deflated beach ball. Do not draw a snake or some other kind or curve!
The Tabs component allows you to organize content into multiple tabbed sections, enabling users to switch between content.
Wrap your content in and use for each individual tab section.
<!-- prettier-ignore -->
<Tabs groupId="language" items={['Rust', 'Typescript']}>
<Tab value="Rust">Rust 1</Tab>
<Tab value="Typescript">Typescript 1</Tab>
</Tabs>
The Accordion component allows you to create collapsible content sections. It's useful for organizing and presenting information in a compact, expandable format.
Wrap your content in and use for each collapsible section.
<Accordion>
<AccordionItem title="Hello">
```ts
console.log("hello");
println!("world");
Content translations are supported via the Crowdin platform.
All the markdown content committed to this repo is in the base language of
English
. On each successful merge of content, all changes are uploaded to
Crowdin (via GitHub actions) for them to be translated into the supported
locales.
During production deployments of the developer content api, all the currently translated content is downloaded from Crowdin and deployed for solana.com to correctly render the user-requested locale of content. Falling back to the base language when no translated content was found.
This developer content repo is a NextJS application that serves markdown-based content as REST API for solana.com to consume. Think of it like a microservice for content.
We call this microservice the "developer content API", or content API for short.
With this repo being only a "content API", there is no web app frontend to view the content within this repo. The frontend UI that will render all the markdown content within this repo is actually solana.com (which is a different repo).
To set up the developer content API:
- Clone the repo to your local machine:
git clone https://github.com/solana-foundation/developer-content.git
cd developer-content
- Install the dependencies via
pnpm
:
pnpm install
- Run the developer content API locally:
pnpm dev
Note: The developer content API normally runs locally on port
3001