This guide covers how to contribute to the Ably docs repository, including general pull request processes and content formatting.
The following high-level information covers the pull request process for Ably docs:
- Pull requests should be raised against the
mainbranch in the majority of cases. - A review site is built for every pull request, and rebuilt on subsequent commits.
- Tag
@team-devedfor review. - Once approved and merged into
main, content is automatically updated on/docs.
CI runs in CircleCI.
Request reviews from the group @team-deved on any pull requests.
CodeRabbitAI doesn't run by default on pull requests, but is available using the command @coderabbitai review.
Contributors should use EditorConfig when adding content. An .editorconfig file is included in the repository.
There is a writing style guide that contributors should follow when adding content.
There are two main locations that contributors need to work with to varying extents. The following is a table explaining what each folder contains that is relevant to editing the docs:
| Folder | Contains |
|---|---|
| how-tos | Tutorial content in MDX format. |
| src | Images, navigation, and language version management. |
Documentation pages use MDX format for content. The following sections discuss common formatting patterns.
The following metadata can be included in the front matter of files. Metadata should be written between --- in YAML:
---
title: "The page title that will appear on page. This is required."
meta_description: "A description for the page that will be returned in searches. This is required."
meta_keywords: "A comma separated list of keywords about the page."
redirect_from: "A YAML array of pages that will redirect to the current page."
---Redirects are automatically added to config/nginx-redirects.conf using the values set in redirect_from, and used to create a map of nginx redirects at build time.
Other one-off instances of redirects are contained in the following files. These should rarely, if ever need to be touched.
/config/website-redirects.conf/config/client-lib-development-guide-redirects.conf/config/nginx.conf.erb
Page headings are implemented in the format: h[1-6](#anchor-id).. In reality h1 is never used as it is automatically set by the page title defined in the metadata.
An example heading (with a custom anchor link) is: h2(#subscribe). Subscribe to a channel
Use standard Markdown link syntax: [link text](url).
To link to another docs page use: [messages](/docs/channels/message).
To link to a heading on the same page: [channel state changes](#listen-for-state).
Use fully qualified URLs: [Ably dashboard](https://ably.com/dashboard).
Note: for dashboard links you can use
/any/as the account ID to link directly to a specific page. For example:https://ably.com/accounts/any/editfor the account settings page.
Codeblocks are defined using triple backticks followed by the language wrapped in square brackets, for example: ```[javascript]. They are closed using triple backticks. Codeblocks are grouped into tabs when they follow one another with a single empty line between them:
```[javascript]
const channel = realtime.channels.get('channelName');
```
```[objc]
ARTRealtimeChannel *channel = [realtime.channels get:@"channelName"];
```
To use nested codeblocks when describing features that are available to the realtime and REST interfaces, use the the same syntax as a normal codeblock but prefix the languages with realtime_ or rest_. Spacing should be continuous as with a normal codeblock, however the snippets for each interface should all be written together, rather than interspersed:
```[realtime_javascript]
const channel = realtime.channels.get('channelName');
```
```[realtime_objc]
ARTRealtimeChannel *channel = [realtime.channels get:@"channelName"];
```
```[rest_javascript]
const channel = rest.channels.get('channelName');
```
```[rest_objc]
ARTRestChannel *channel = [realtime.channels get:@"channelName"];
```
Add highlight="..." after the language identifier to highlight specific lines. Supports individual lines and ranges, with optional + (addition/green) or - (removal/orange) prefixes. Unprefixed lines get a neutral blue highlight.
```javascript highlight="+1-2,3,-5-6,7-8"
const client = new Ably.Realtime('your-api-key');
const channel = client.channels.get('my-channel');
channel.unsubscribe();
// This line has no highlight
console.log('done');
console.log('highlighted');
console.log('neutral range start');
console.log('neutral range end');
```
Syntax:
3: highlight line 3 (blue)1-6: highlight lines 1 through 6 (blue)+3or+1-6: addition highlight (green)-3or-1-6: removal highlight (orange)- Comma-separated for multiple specs
Note: The highlight meta is stripped from code fences when compiling markdown for LLMs.
In-line code should be written between @ symbols. For example, the @get()@ method.
The following variables can be inserted into codeblocks to generate values at runtime:
| Variable | Description |
|---|---|
{{API_KEY}} |
Injects a demo API key into the code sample if a user isn't logged in. Enables users to select an API key from from their dashboard if they are logged in. |
{{RANDOM_CHANNEL_NAME}} |
Generates a random channel name. |
Definition lists are used to display a list of terms and their descriptions, such as the available capabilities.
They use the following syntax:
- publish := can publish messages to channels
- presence := can register presence on a channel (enter, update and leave)
Tables use standard Markdown syntax:
| Flag | Description |
| ---- | ----------- |
| SUBSCRIBE | Can subscribe to receive messages on the channel. |
For more complex table layouts, HTML table syntax can be used.
Nested tables are used to display API reference types and enums.
This new style will be enabled when you wrap a normal table in <Table> tags. They use the following special properties:
| Property | Description |
|---|---|
id |
The identifier for that table so that it can be referenced in another table. |
hidden |
If present, the table will not appear in the rendered content. It will however be present when you view the page as markdown, just with this property removed. This is to simplify the approach for LLMs and ensures the content is present and used. |
The following is an example of using this new format. Only TypingOptions is included for brevity:
<Table id='RoomOptions'>
| Property | Required | Description | Type |
| --- | --- | --- | --- |
| typing | optional | Configuration for typing indicators. | <Table id='TypingOptions'/> |
| presence | optional | Configuration for presence events. | <Table id='PresenceOptions'/> |
| occupancy | optional | Configuration for occupancy events. | <Table id='OccupancyOptions'/> |
| messages | optional | Configuration for message reactions. | <Table id='MessagesOptions'/> |
</Table>
<Table id='TypingOptions' hidden>
| Property | Required | Description | Type |
| --- | --- | --- | --- |
| heartbeatThrottleMs | optional | Minimum time in milliseconds between consecutive typing started events. The first call emits immediately; later calls are no-ops until the interval has elapsed. Calling typing.stop resets the interval. Default 10000. | number |
</Table>Nested tables use the following columns and they must be in this order:
| Column | Description |
|---|---|
| name | The name of the fields, e.g. Attributes, Properties or Members etc. |
Description |
A description of the property. |
Required |
Adds a tag showing whether a property is required or not. Permitted values are optional and required. |
Type |
Adds a label as to the type of property and renders that as plain text. If a <Table> tag is embedded with an id then renders that table nested. |
Note that both the Required and Type columns can be omitted when show a list of enums for example.
There are three types of admonition that can be used; note, important and further-reading. Update the value of data-type to switch between them. Admonitions are written using the HTML <aside> tag with HTML or Markdown content.
<aside data-type='note'>
<p>The following restrictions apply to "channel":/docs/channels names:</p>
<ul><li>Channel names are case sensitive</li>
<li>They can't start with @[@ or @:@</li>
<li>They can't be empty</li>
<li>They can't contain newline characters</li></ul>
</aside>Note: 'important' admonitions should be used sparingly, and only for critical information. Try to keep admonitions short where possible. They may not contain block code, only in-line code.
Three additional types of admonition are used to display features that have been added or updated in a version update; new, updated and experimental.
<aside data-type='updated'>
<p>The @AblyProvider@ was updated in version 2.0. See the "migration guide":https://github.com/ably/ably-js/blob/main/docs/migration-guides/v2/react-hooks.md#rename-optional-id-field-to-ablyid for details on upgrading from a previous version.</p>
</aside>Use * for unordered lists for consistency.
* Strawberry
* Mango
* Mint Choc Chip
For ordered lists, use standard Markdown syntax with ascending numbers for readability and consistency.
1. Cornetto
2. Twister
3. Mars
Images are implemented using the HTML <a> tag. They are all stored in the images folder and should be named named logically. The root of this folder can be accessed using @content/.
<a href="@content/diagrams/history-default.png" target="_blank">
<img src="@content/diagrams/history-default.png" style="width: 100%" alt="Default Persistence">
</a>
There are three different ways of displaying different text depending on the language that someone has selected.
A blang[]. enables you to provide blocks of content that only appear when a specific set of languages are selected. They can also be chained together in succession. The content that follows a blang[]. must be indented by one tab. The first sentence that isn't indented will revert to being visible to all languages.
blang[ruby].
This sentence is only visible when Ruby is selected.
blang[csharp,python].
This sentence is visible when C# or Python is selected.
This sentence will be visible regardless of which language is selected.
<span> tags can be used to display a word, or short phrase in different languages. They are quite messy to read, so are mainly used in API references to label property names differently.
<span lang="ruby">@Ably::Util::@</span><span lang="java">@io.ably.lib.util.@</span><div> tags can be used in a similar fashion to blang[]., however they aren't indented.
<div lang="java,swift,objc">
h6(#device).
A reference to the "<span lang="default">@LocalDevice@</span><span lang="objc,swift">@ARTLocalDevice@</span>":/api/realtime-sdk/push#local-device object.
</div>SDKs are versioned using SemVer. When a new major or minor version is released, the documentation needs to be updated to reflect the change and the version bumped in src/data/languages/languageData.ts.
The list of available languages that can be used throughout the site are in src/data/languages/languageInfo.ts.
The left-hand navigation is written per-product and generated using the files in src/data/nav.
export default {
name: 'The top-level product name that appears in the navigation.',
link: 'The link of the page you want the product name to be in the breadcrumbs, e.g. /chat',
icon: {
closed: 'The monochrome product image in the navigation, e.g. icon-product-chat-mono',
open: 'The coloured product image in the navigation when it is open, e.g. icon-product-chat',
},
...
content: [
{
name: 'A section header that appears in capitals, e.g. Introduction',
pages: [
{
name: 'The page name to appear in the navigation, e.g. About Chat',
link: 'The link to the page referenced above, e.g. /chat',
},
],
},
...
api: [
{
name: 'The title for the API reference section, e.g. API References',
pages: [
{
name: 'The page name to appear in the navigation, e.g. JavaScript SDK',
link: ' The link to the page reference above, e.g. https://sdk.ably.com/builds/ably/ably-chat-js/main/typedoc/modules/chat-js.html',
external: 'Whether the link is external to the docs site, so an arrow is appended to the name. This defaults to false and is boolean, e.g.' true,
},
...The homepage content is in src/data/content/homepage.ts.
The majority of the content above is also relevant for API references, however there are some variations and additional items to be aware of.
Properties and methods should be written as H6 headings so that they are styled appropriately. These headings can also take an optional tabbed set of languages if the method name differs between languages.
h6(#get).
default: get
csharp: Get
Method signatures are written in a similar way to headings. They use the bq(definition). syntax and can list options for each language, or languages.
bq(definition).
default: new Ably.Realtime(String keyOrTokenId)
ruby: Ably::Realtime.new(String key_or_token_id)
API references previously used partials for reusable content. This functionality has been replaced with component-based content reuse.
<%= partial partial_version('realtime/_stats') %>
The Control API is a REST API and the spec can be found in `static/open-specs/control-v1.yaml.
This is then referenced in `src/pages/api/control-api.tsx for rendering.
The examples directory contains feature-specific demonstrations of how to use Ably in different environments, organized by framework. Each example resides in a structured path like /examples/<feature>/react or /examples/<feature>/javascript, showcasing practical implementations for various use cases.
- Create a new directory for your example under
/examples, this should be named after the feature/example you're building, e.g.chat-typing-indicator. - Inside this directory, create a subdirectory named after the language or framework (e.g.,
javascript,react). You can have bothjavascriptandreactsubdirectories within the same example directory.
Currently, we support Javascript (Typescript) and React, both using Vite.
To create a new project using Vite, follow the instructions below for either JavaScript/TypeScript or React.
- Open your terminal.
- Navigate to the directory where you want to create your project.
- Run the following command to create a new Vite project:
npm create vite@latest javascript -- --template vanilla- Navigate into your project directory:
cd javascript- Open your terminal.
- Navigate to the directory where you want to create your project.
- Run the following command to create a new Vite project with React template:
npm create vite@latest react -- --template react- Navigate into your project directory:
cd reactAfter following these steps, you will have a Vite project set up with JavaScript/TypeScript or React, ready for further development.
Each example must include a README.md file with instructions to get the developer running the example locally as quick as possible. These instructions should follow the structure below:
- Introduction: Describe the Ably features used, what problem is this solving for the intended audience?
- Resources: List components and functions from the Ably SDKs and their purposes.
- Getting started: Provide step-by-step instructions:
- Clone the docs repository.
- Navigate to the examples directory.
- Rename environment variable files. (
.env.example->.env.local) - Update environment variables.
- Install dependencies with
yarn install. - Run the project.
- Opening in CodeSandbox: Instructions for opening the example in CodeSandbox. (At this moment, this heading contains the renaming of the environment variables)
Add entries for each example to the /examples/package.json file under workspaces and scripts, an example of this is shown below:
"workspaces": [
"chat-typing-indicator/react",
"chat-typing-indicator/javascript",
],
"scripts": {
"chat-typing-indicator-javascript": "yarn workspace chat-typing-indicator-javascript dev",
"chat-typing-indicator-react": "yarn workspace chat-typing-indicator-react dev",
},The intention of the scripts entries is to make the command simpler for the developer to run locally.
All examples use config from the examples root directory. Update these files inside your project directory:
-
vite.config.ts:import { defineConfig } from 'vite'; import baseConfig from '../../vite.config'; export default defineConfig({ ...baseConfig, envDir: '../../', });
-
tailwind.config.ts:import baseConfig from '../../tailwind.config'; import type { Config } from 'tailwindcss'; const config: Config = { ...baseConfig, content: ['./src/**/*.{js,ts,tsx}', './index.html'], }; export default config;
-
tsconfig.json:{ "extends": "../../tsconfig.json" } -
vite-env.d.ts(Javascript only):interface ImportMetaEnv { readonly VITE_ABLY_KEY: string; } interface ImportMeta { readonly env: ImportMetaEnv; }
For styling consistency purposes, each example will need to use Franken-ui for components, and any other styling to use tailwindcss. Some examples for this are the button and input components below:
Button:
<button class="uk-btn uk-btn-md uk-btn-primary py-2 rounded">
Submit
</button>Input:
<input type="text" class="uk-input uk-width-1-1 uk-border-rounded-left">