MDX components

There are some nifty MDX components available for use in Markdown content. These components are included globally, so you don't need to do anything special to use them (like renaming .md to .mdx or manually importing them at the top of the file).

Images

Product screenshots

The <ProductScreenshot /> component encapsulates an image with a border and background. It's useful since the app's background matches the website background, and without using this component, it can be hard to differentiate between the screenshot and normal page content. It also optionally supports dark mode screenshots.

You use it by passing image URLs to the imageLight and imageDark props like this:

TSX
<ProductScreenshot
imageLight="https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/handbook/images/tutorials/limit-session-recordings/sampling-config-light.png"
imageDark="https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/handbook/images/tutorials/limit-session-recordings/sampling-config-dark.png"
alt="Sampling config shown set to 100% i.e. no sampling"
classes="rounded"
/>

Optionally pass zoom={false} if you don't want the image to be zoomable, otherwise it will be zoomable by default.

Note: If you don't have a dark image, just leave out the imageDark prop and the light screenshot will be used for both color modes.

Image slider

You can create a slider or carousel of images by wrapping them in the <ImageSlider> component like this:

HTML
<ImageSlider>
![posthog](https://res.cloudinary.com/dmukukwp6/image/upload/v1710055416/posthog.com/contents/images/screenshots/hogflix-dashboard.png)
![posthog](https://res.cloudinary.com/dmukukwp6/image/upload/v1710055416/posthog.com/contents/images/screenshots/hogflix-dashboard.png)
</ImageSlider>

See an example in our open-source analytics tools post.

Product videos

Th <ProductVideo /> component works the same as product screenshots (above) for videos uploaded to Cloudinary but supports light and dark videos.

  1. Import the video(s) at the top of the post (directly following the MDX file's frontmatter and dashes):
HTML
---
export const NewFunnelLight = "https://res.cloudinary.com/dmukukwp6/video/upload/posthog.com/contents/handbook/images/docs/user-guides/funnels/new-funnel.mp4"
export const NewFunnelDark = "https://res.cloudinary.com/dmukukwp6/video/upload/posthog.com/contents/handbook/images/docs/user-guides/funnels/new-funnel-dark.mp4"
  1. Use the component wherever you want the video(s) to appear.
TSX
<ProductVideo
videoLight={NewFunnelLight}
videoDark={NewFunnelDark}
classes="rounded"
/>

Note: If you don't have a dark video, just leave out the videoDark prop and the light video will be used for both color modes.

Embedding YouTube videos

While not an MDX component, a reminder that when embedding a YouTube video, you should do two things:

  1. Use the -nocookie variant of the YouTube URL. eg:
    https://www.youtube-nocookie.com/embed/{VIDEO_ID}
  2. Add the allowfullscreen attribute to the iframe so users have the option to watch the video in fullscreen (useful for reading code snippets).

Code blocks

The PostHog website has a custom code block component that comes with a number of useful features built-in:

Basic codeblock

Codeblocks in PostHog are created by enclosing your snippet using three backticks (```) or three tildes (~~~), as shown below:

MDX
```
{
"name": "Max, Hedgehog in Residence",
"age": 2
}
```

This will produce the following codeblock:

{
"name": "Max, Hedgehog in Residence",
"age": 2
}

Adding syntax highlighting

Syntax highlighting can be added by specifying a language for the codeblock, which is done by appending the name of the language directly after the opening backticks or tildes as shown below.

MDX
```json
{
"name": "Max, Hedgehog in Residence",
"age": 2
}
```

This will produce the following output:

JSON
{
"name": "Max, Hedgehog in Residence",
"age": 2
}

Using tabs

You can use the <Tab /> component to create tabs in your code blocks. This is useful for showing multiple code snippets or examples in a single code block.

JavaScript
console.log('Hello, world!')

Supported languages

Here is a list of all the languages that are supported in codeblocks:

Frontend

HTMLhtml
CSS / SCSS / LESScss / less
JavaScriptjs
JSXjsx
TypeScriptts
TSXtsx
Swiftswift
Dartdart
Objective-Cobjectivec

Backend

Node.jsnode
Elixirelixir
Golanggo
Javajava
PHPphp
Rubyruby
Pythonpython
C / C++c / cpp

Misc.

Terminalbash or shell
JSONjson
XMLxml
SQLsql
GraphQLgraphql
Markdownmarkdown
MDXmdx
YAMLyaml
Gitgit

Note: If you want syntax highlighting for a snippet in another language, feel free to add your language to the imports here and open a PR.

Multi-language code blocks

You can use the <MultiLanguage> component to show code blocks in multiple languages.

console.log('Hello, world!')

Multiple code snippets in one block

With PostHog's MultiLanguage component, it's possible to group multiple code snippets together into a single block.

MDX
<MultiLanguage>
```js
console.log('Hello world!')
```
```html
<div>Hello world!</div>
```
</MultiLanguage>

Note: Make sure to include empty lines between all your code snippets, as well as above and below the MultiLanguage tag

This will render the following codeblock:

console.log('Hello world!')

Specifying which file a snippet is from

You can specify a filename that a code snippet belongs to using the file parameter, which will be displayed in the top bar of the block.

MDX
```yaml file=values.yaml
cloud: 'aws'
ingress:
hostname: <your-hostname>
nginx:
enabled: true
cert-manager:
enabled: true
```

Note: Make sure not to surround your filename in quotes. Each parameter-value pair is delimited by spaces.

This produces the following codeblock:

values.yaml
cloud: 'aws'
ingress:
hostname: <your-hostname>
nginx:
enabled: true
cert-manager:
enabled: true

Code highlighting

Especially in long tutorials, you can highlight the important differences between steps using highlighting comments. It's much easier to read visual diffs than reading through the code block line by line.

CommentEffectUsage
// +Green highlightRepresents additions in diffs
// -Red highlightRepresents removals in diffs
// HIGHLIGHTYellow highlightGeneral emphasis without special meaning
JavaScript
const a = 1
const b = 2
const c = a + b
console.log(a + b)
console.log(c)
console.log('end')

Collapsed code blocks

In some cases, such as large nested config files, you need readers to focus on a specific part of the code block while maintaining the context. You can do this by adding focusOnLines= to the code block. This collapses the code block and only shows the lines of code you specify.

angular.json
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"sourceMap": {
"scripts": true,
"styles": true,
"hidden": true,
"vendor": true
}
}
}

Mermaid diagrams

Code blocks can also be used to show mermaid UML diagrams. When using these diagrams, make sure to include a text description of the diagram afterwards for accessibility and LLMs.

Call to action

Adding <ArrayCTA /> to any article will add this simple CTA:

Want to just try it already?

(Sorry for the shameless CTA.)

Don't overuse it, but it's useful for high intent pages, like comparisons.

Feature comparison tables

When comparing features between two or more products, use the <ProductComparisonTable /> component which sources data from the src/hooks/competitorData/ directory and lets you compare specific features across multiple competitors.

TSX
<ProductComparisonTable
competitors={['posthog', 'amplitude']}
rows={['product_analytics']}
/>

Read more in the product & feature comparisons handbook page.

Captions

You can add captions below images using the following code:

TSX
<Caption>Add you caption copy here</Caption>

Here's an example of what it looks like:

PostHog webshare pricing experiment

Adding the 'Buy Now' call to action and adjusting the text enabled Webshare to boost conversion by 26%

Customer quotes

Add a styled quote component using the following code:

Viktor Eriksson
Viktor Eriksson
Software Engineer, Lovable
PostHog is super cool because it is such a broad platform. If you're building a new product or at a startup, it's a no-brainer to use PostHog. It's the only all-in -one platform like it for developers.

Product-specific quote

JSX
<OSQuote
customer="significa"
author="tomas_gouveia"
product="web_analytics"
/>
Tomás Gouveia
Tomás Gouveia
Digital Marketer, Significa
PostHog gives me all the same information Plausible used to give us, and a lot more. It's way more powerful and insightful than Plausible.

Generic quote

JSX
<OSQuote
customer="lovable"
author="viktor_eriksson"
quote={0}
/>

We mainly use them in customer stories and some product pages.

Quotes are sourced from the useCustomers hook and can reference product-specific quotes or general quotes by someone at a company. Be sure to add the customer's information to the useCustomers hook in src/hooks/useCustomers.tsx.

Example

TSX
quotes: {
viktor_eriksson: {
name: 'Viktor Eriksson',
role: 'Software Engineer',
image: {
thumb: 'https://res.cloudinary.com/dmukukwp6/image/upload/q_auto,f_auto/viktor_00c779a706.jpg',
},
quotes: [
"PostHog is super cool because it is such a broad platform. If you're building a new product or at a startup, it's a no-brainer to use PostHog. It's the only all-in -one platform like it for developers.",
],
},
},

Collapsible sections

The combination of <details> and <summary> components enables you to add a collapsible section to your page. Useful for FAQs or details not relevant to the main content.

HTML
<details>
<summary>Can I specify some events to be identified and others to be anonymous for the same users?</summary>
Not if you already identified them. Once a user is identified, all _future_ events for that user are associated with
their person profile and are captured as identified events.
</details>

Tabs

Tabs enable you to display different content in a single section. We often use them to show different code examples for different languages, like in installation pages.

To use them:

  1. Import the Tab component.
  2. Set up Tab.Group, Tab.List, and Tab.Panel for each tab you want to display. The tabs prop in Tab.Group should be an array of strings, one for each tab. This enables you to link to each tab by its name.
  3. Add the content for each tab in the Tab.Panel components. You should use snippets for readability, maintainability, and to avoid duplication, but you can use multiple snippets in a single tab.

For example, here's how we set up the tabs for the error tracking installation page:

JSX
import Tab from "components/Tab"
import WebInstall from '../integrate/_snippets/install-web.mdx'
import JSWebErrorTracking from './_snippets/web-install-error-tracking.mdx'
import PythonInstall from '../integrate/_snippets/install-python.mdx'
import PythonErrorTracking from './_snippets/python-install-error-tracking.mdx'
import NextJSErrorTracking from './_snippets/nextjs-install-error-tracking.mdx'
import UploadSourceMaps from './_snippets/upload-source-maps.mdx'
import UploadSourceMapsSteps from './_snippets/upload-source-maps-steps.mdx'
Error tracking enables you to track, investigate, and resolve exceptions your customers face. Getting this working requires installing PostHog:
<!-- prettier-ignore -->
<Tab.Group tabs={['Web', 'Next.js', 'Python']}>
<Tab.List>
<Tab>Web</Tab>
<Tab>Next.js</Tab>
<Tab>Python</Tab>
</Tab.List>
<Tab.Panels>
<Tab.Panel>
<WebInstall />
<JSWebErrorTracking />
<UploadSourceMaps />
<UploadSourceMapsSteps />
</Tab.Panel>
<Tab.Panel>
<NextJSErrorTracking />
</Tab.Panel>
<Tab.Panel>
<PythonInstall />
<PythonErrorTracking />
</Tab.Panel>
</Tab.Panels>
</Tab.Group>

You can default to a specific tab by passing the tab name in the query string like:

/docs/product-analytics/installation?tab=web

Linking internally

Use Markdown's standard syntax for linking internally.

[Link text](/absolute-path/to/url)

Be sure to use relative links (exclude https://posthog.com) with absolute paths (reference the root of the domain with a preceding /).

Correct syntax/absolute-path/to/url
Incorrect syntaxhttps://posthog.com/absolute-path/to/url

Open a new PostHog window

To open a link in a new window within the PostHog.com OS interface, use state={{ newWindow: true }} like:

TSX
<Link to="/absolute-path/to/url" state={{ newWindow: true }}>
Link text
</Link>

Linking externally

The <Link /> component is used throughout the site, and is accessible within Markdown. (When used internally, it takes advantage of Gatsby's <Link /> features like prefetching and client-side navigation between routes).

While that doesn't apply here, using it comes with some handy parameters that you can see in action via the link above:

  • Add external to a) open the link in a new tab, and b) add the external link icon (for UX best practices if forcing a link to open in a new window)
  • If, for some reason, you need to hide the icon, use externalNoIcon instead

Example:

<Link to="#" external>
click here
</Link>

Sometimes we link to confidential information in our handbook. Since the handbook is public, it's useful to indicate when a link is private so visitors aren't confused as to why they can't access a URL (like a Slack link or private GitHub repo). Use the <PrivateLink /> component for this. See an example (on our share options page.)

<PrivateLink url="https://path/to/private/link">
click here
</PrivateLink>

Private links will always open in a new browser tab.

Mention a team member

Use this component to mention a team member in a post. It will link to their community profile and appears like this: Cory Watilo

<TeamMember name="Cory Watilo" />

There's also a photo parameter which will inline their photo next to their name like this: Cory Watilo

Mention a small team

Use this component to mention a small team in a post. It will link to their team page and appears like this:

Brand Team
Brand mini crest
Brand Team

<SmallTeam slug="brand" />

The default version shows the team's mini crest and name in a bordered "chip" style. There's also a noMiniCrest parameter to omit the mini crest and border for inline usage like this: Brand Team

<SmallTeam slug="brand" noMiniCrest />

Both versions will show the full team crest on hover. Clicking the tooltip will open the team page in a new window.

Embedded posts

You can embed what looks like a Tweet an X post using the <Tweet> component. It's used on the terms and privacy policy pages, but was componentized for use in blog posts to break up bullet points at the top of the post.

Note: This does not actually embed an X post; it's just styled to look like one.

James ("Veg"/"JC") Hawkins
James Hawkins
Here's what a post looks like. It's designed to have a familiar look that makes it easy to scan.
James ("Veg"/"JC") Hawkins
James Hawkins
If you show multiple posts in a row, they'll be connected by a vertical line to make it look like a thread.

Usage

Be sure to change the alert message which appears if you click one of the action buttons (reply, repost, like).

TSX
<Tweet
className="mx-auto"
alertMessage="Gen Z? Don't get distracted. You're here to read our exciting embedded post component."
>
If you show multiple posts in a row, they'll be connected by a vertical line to make it look like a thread.
</Tweet>

You can optionally center the post with the mx-auto class (shown in the example code, but not used in the preview above).

Community questions

Was this page useful?

Questions about this page? or post a community question.