Skip to content

Commit 152aca5

Browse files
kgowrudevin-ai-integration[bot]devalog
authored
Document redesigned dynamic OG images with toggle flags and color overrides (#4322)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Devin Logan <devinannlogan@gmail.com>
1 parent 8c9a9dd commit 152aca5

3 files changed

Lines changed: 191 additions & 84 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
tags: ["seo", "configuration"]
3+
---
4+
5+
## Redesigned dynamic OG images
6+
7+
Dynamic OG images have a new layout with fine-grained control over the logo variant, text and background colors, and which elements appear (section, description, URL, gradient).
8+
9+
```yaml docs.yml
10+
metadata:
11+
og:dynamic: true
12+
og:background-image: ./images/og-background.png
13+
og:dynamic:text-color: "#1a1a1a"
14+
og:dynamic:background-color: "#ffffff"
15+
og:dynamic:logo-color: dark
16+
og:dynamic:show-logo: true
17+
og:dynamic:show-section: true
18+
og:dynamic:show-description: true
19+
og:dynamic:show-url: true
20+
og:dynamic:show-gradient: true
21+
```
22+
23+
<Button intent="none" outlined rightIcon="arrow-right" href="/learn/docs/seo/setting-seo-metadata#dynamic-og-images">Read the docs</Button>

fern/products/docs/pages/seo/metadata.mdx

Lines changed: 105 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Configure SEO metadata
33
description: Configure SEO metadata in Fern docs with page-level frontmatter and site-wide settings. Control titles, descriptions, social media previews, and sitemap timestamps.
4-
max-toc-depth: 3
4+
max-toc-depth: 4
55
---
66

77
When you want to customize how your pages appear in search results or social previews, you can set defaults [at the site level](#site-wide-defaults) or override them on [individual pages](#page-level-overrides).
@@ -63,91 +63,135 @@ Identity and descriptive fields used by search engines and shared across social
6363
The name of your website for Open Graph tags.
6464
</ParamField>
6565

66-
<ParamField path="metadata.og:title" type="string" required={false} toc={true}>
67-
The title shown in social media previews.
66+
<ParamField path="metadata.og:title" type="string" required={false} default="Page title" toc={true}>
67+
The title shown in social media previews. Falls back to the page's `title` when unset.
6868
</ParamField>
6969

70-
<ParamField path="metadata.og:description" type="string" required={false} toc={true}>
71-
The description shown in social media previews.
70+
<ParamField path="metadata.og:description" type="string" required={false} default="Page description" toc={true}>
71+
The description shown in social media previews. Falls back to the page's `description`, `subtitle`, or `excerpt` when unset.
7272
</ParamField>
7373

74-
<ParamField path="metadata.og:url" type="string" required={false} toc={true}>
75-
The canonical URL of your documentation.
74+
<ParamField path="metadata.og:url" type="string" required={false} default="Page URL" toc={true}>
75+
The canonical URL of your documentation. Falls back to the page's resolved URL when unset.
7676
</ParamField>
7777

7878
<ParamField path="metadata.og:locale" type="string" required={false} toc={true}>
79-
The locale of your content (e.g., `en_US`).
79+
The locale of your content (e.g., `en_US`). No default; the tag is omitted when unset.
8080
</ParamField>
8181

82-
<ParamField path="metadata.canonical-host" type="string" required={false} toc={true}>
82+
<ParamField path="metadata.canonical-host" type="string" required={false} default="Instance URL" toc={true}>
8383
The host of your documentation website. Used to set the canonical URL for metadata tags and documents like the sitemap. Defaults to the URL defined in `instances`.
8484
</ParamField>
8585

8686
### Social image
8787

88-
The image displayed when your docs are shared on LinkedIn, Slack, Discord, and other platforms. Use a 1200x630px image for the best display across platforms — this is the standard Open Graph size and will render correctly in most previews. Avoid embedding text in the image since it may be cropped on some platforms.
88+
The image displayed when your docs are shared on LinkedIn, Slack, Discord, and other platforms. You can either set a single image that applies to every page, or have Fern dynamically generate a unique image per page.
89+
90+
#### Manual
91+
92+
Set one static image with `og:image` that applies to every page. Use a 1200x630px image for the best display across platforms — this is the standard Open Graph size and will render correctly in most previews. Avoid embedding text in the image since it may be cropped on some platforms.
8993

9094
<ParamField path="metadata.og:image" type="string" required={false} toc={true}>
91-
The image shown in social media previews. Recommended size is 1200x630 pixels.
95+
The image shown in social media previews. Recommended size is 1200x630 pixels. No default; the tag is omitted when unset.
9296
</ParamField>
9397

9498
<ParamField path="metadata.og:image:width" type="number" required={false} toc={true}>
95-
The width of your Open Graph image in pixels.
99+
The width of your Open Graph image in pixels. Only applied when `og:image` is set.
96100
</ParamField>
97101

98102
<ParamField path="metadata.og:image:height" type="number" required={false} toc={true}>
99-
The height of your Open Graph image in pixels.
103+
The height of your Open Graph image in pixels. Only applied when `og:image` is set.
100104
</ParamField>
101105

102106
<ParamField path="metadata.og:logo" type="string" required={false} toc={true}>
103-
URL to your company logo.
107+
URL to your company logo. No default; the tag is omitted when unset.
104108
</ParamField>
105109

106-
### Dynamic OG images <Availability type="beta" /> [#dynamic-og-images]
110+
#### Dynamic <Availability type="beta" /> [#dynamic-og-images]
107111

108-
Instead of using a single static image for all pages, you can enable dynamic OG image generation. When enabled, Fern automatically generates a unique `og:image` for each page that doesn't have one [set in frontmatter](#open-graph).
112+
Instead of using a single static image for all pages, you can enable dynamic OG image generation. When enabled, Fern automatically generates a unique `og:image` for each page that doesn't have one [set in frontmatter](#open-graph). The `og:dynamic:*` sub-settings and `og:background-image` below are only read when `og:dynamic: true` — they're ignored otherwise. `fern check` surfaces warnings for conflicting settings so you can resolve them locally.
109113

110114
You can optionally provide a custom background image (`og:background-image`) for dynamically generated OG images.
111115

112116
```yaml docs.yml
113117
metadata:
114118
og:dynamic: true
115-
og:background-image: ./images/og-background.png # optional
119+
og:background-image: ./images/og-background.png
120+
og:dynamic:text-color: "#1a1a1a"
121+
og:dynamic:background-color: "#ffffff"
122+
og:dynamic:logo-color: dark
123+
og:dynamic:show-logo: true
124+
og:dynamic:show-section: true
125+
og:dynamic:show-description: true
126+
og:dynamic:show-url: true
127+
og:dynamic:show-gradient: true
116128
```
117129

118130
<ParamField path="metadata.og:dynamic" type="boolean" required={false} default={false} toc={true}>
119-
When `true`, enables dynamic OG image generation for pages that don't have a custom `og:image` set.
131+
When `true`, enables dynamic OG image generation for pages that don't have a custom `og:image` set. Any site-wide `og:image` and `twitter:image` still apply to the homepage; every other page uses the dynamically generated image.
120132
</ParamField>
121133

122134
<ParamField path="metadata.og:background-image" type="string" required={false} toc={true}>
123-
A custom background image for dynamically generated OG images. Can be a URL or a relative file path. Relative paths are resolved from the YAML file where this property is set (e.g., `docs.yml`). When set, this image is used as the background instead of a solid color.
135+
A custom background image for dynamically generated OG images. Can be a URL or a relative file path. Relative paths are resolved from the YAML file where this property is set (e.g., `docs.yml`). When set, this image is used as the background instead of a solid color. No default; the dynamic OG image renders without a background image when unset.
136+
</ParamField>
137+
138+
<ParamField path="metadata.og:dynamic:text-color" type="string" required={false} default="#ffffff (dark) / #1a1a1a (light)" toc={true}>
139+
Override the text color for dynamically generated OG images. Accepts any valid CSS color value (e.g., `"#1a1a1a"`). By default, Fern reads the text color from your theme (`grayScale[11]`). If no theme color is available, falls back to `#ffffff` for dark mode or `#1a1a1a` for light mode. Must differ from `og:dynamic:background-color` so the text remains visible.
140+
</ParamField>
141+
142+
<ParamField path="metadata.og:dynamic:background-color" type="string" required={false} default="#0A0A0A (dark) / theme background (light)" toc={true}>
143+
Override the background color for dynamically generated OG images. Accepts any valid CSS color value (e.g., `"#ffffff"`). By default, Fern reads the background color from your theme. If no theme color is available, falls back to `#0A0A0A`.
144+
</ParamField>
145+
146+
<ParamField path="metadata.og:dynamic:logo-color" type="enum" required={false} default="dark" toc={true}>
147+
Choose which logo variant to render in dynamically generated OG images. Accepts `dark` or `light`, matching the corresponding entry under the top-level [`logo:` setting](/learn/docs/getting-started/global-configuration#logo-configuration) in your `docs.yml`. If your `docs.yml` only defines one logo variant, that variant is used regardless of this setting. Has no effect when `og:dynamic:show-logo: false`.
148+
</ParamField>
149+
150+
<ParamField path="metadata.og:dynamic:show-logo" type="boolean" required={false} default={true} toc={true}>
151+
Toggle visibility of the logo in dynamically generated OG images. Defaults to `true` when `og:dynamic` is enabled.
152+
</ParamField>
153+
154+
<ParamField path="metadata.og:dynamic:show-section" type="boolean" required={false} default={true} toc={true}>
155+
Toggle visibility of the section title in dynamically generated OG images. The section title is derived from the page's navigation breadcrumb. Defaults to `true` when `og:dynamic` is enabled.
156+
</ParamField>
157+
158+
<ParamField path="metadata.og:dynamic:show-description" type="boolean" required={false} default={true} toc={true}>
159+
Toggle visibility of the page description in dynamically generated OG images. The description is extracted from the page's frontmatter (`description`, `subtitle`, or `excerpt`). Defaults to `true` when `og:dynamic` is enabled.
160+
</ParamField>
161+
162+
<ParamField path="metadata.og:dynamic:show-url" type="boolean" required={false} default={true} toc={true}>
163+
Toggle visibility of the page URL in dynamically generated OG images. Defaults to `true` when `og:dynamic` is enabled.
164+
</ParamField>
165+
166+
<ParamField path="metadata.og:dynamic:show-gradient" type="boolean" required={false} default={true} toc={true}>
167+
Toggle visibility of the accent gradient overlay in dynamically generated OG images. The gradient uses your accent color. Defaults to `true` when `og:dynamic` is enabled.
124168
</ParamField>
125169

126170
### Twitter / X
127171

128172
Controls how your docs appear in Twitter Card previews when shared on X.
129173

130-
<ParamField path="metadata.twitter:title" type="string" required={false} toc={true}>
131-
The title shown in Twitter Card previews.
174+
<ParamField path="metadata.twitter:title" type="string" required={false} default="og:title" toc={true}>
175+
The title shown in Twitter Card previews. Falls back to `og:title` (and then to the page title) when unset.
132176
</ParamField>
133177

134-
<ParamField path="metadata.twitter:description" type="string" required={false} toc={true}>
135-
The description shown in Twitter Card previews.
178+
<ParamField path="metadata.twitter:description" type="string" required={false} default="og:description" toc={true}>
179+
The description shown in Twitter Card previews. Falls back to `og:description` (and then to the page description) when unset.
136180
</ParamField>
137181

138182
<ParamField path="metadata.twitter:handle" type="string" required={false} toc={true}>
139-
Your company's Twitter handle.
183+
Your company's Twitter handle. No default; the tag is omitted when unset.
140184
</ParamField>
141185

142-
<ParamField path="metadata.twitter:image" type="string" required={false} toc={true}>
143-
The image shown in Twitter Card previews.
186+
<ParamField path="metadata.twitter:image" type="string" required={false} default="og:image" toc={true}>
187+
The image shown in Twitter Card previews. Falls back to `og:image` when unset.
144188
</ParamField>
145189

146190
<ParamField path="metadata.twitter:site" type="string" required={false} toc={true}>
147-
The Twitter handle for your website.
191+
The Twitter handle for your website. No default; the tag is omitted when unset.
148192
</ParamField>
149193

150-
<ParamField path="metadata.twitter:card" type="string" required={false} toc={true}>
194+
<ParamField path="metadata.twitter:card" type="string" required={false} default="summary_large_image" toc={true}>
151195
The Twitter Card type. Options are `summary`, `summary_large_image`, `app`, or `player`.
152196
</ParamField>
153197

@@ -181,87 +225,87 @@ nofollow: false
181225

182226
Page title, URL, and keyword fields used by search engines. Use `headline` when you need a different title for search engines than what appears as the visible page heading.
183227

184-
<ParamField path="headline" type="string" required={false} toc={true}>
228+
<ParamField path="headline" type="string" required={false} default="Page title with site name" toc={true}>
185229
When set, the `<title />` tag in the document head will use this value rather than the `title` property. For example, your `title` might be "Quickstart" (shown in the sidebar and as the H1), while `headline` could be "Quickstart | PlantStore API Docs" to give search engines more context. If not set, Fern uses `title` with your site name appended.
186230
</ParamField>
187231

188-
<ParamField path="canonical-url" type="string" required={false} toc={true}>
189-
Overrides the canonical URL for this page. Must be a full URL including the protocol (e.g., `https://buildwithfern.com/learn/docs/content/frontmatter`).
232+
<ParamField path="canonical-url" type="string" required={false} default="Page URL" toc={true}>
233+
Overrides the canonical URL for this page. Must be a full URL including the protocol (e.g., `https://buildwithfern.com/learn/docs/content/frontmatter`). Defaults to the page's resolved URL when unset.
190234
</ParamField>
191235

192236
<ParamField path="keywords" type="string" required={false} toc={true}>
193-
Comma-separated keywords relevant to the page (e.g., `plants, garden, nursery`). Accepts only comma-separated strings, not arrays.
237+
Comma-separated keywords relevant to the page (e.g., `plants, garden, nursery`). Accepts only comma-separated strings, not arrays. No default; the tag is omitted when unset.
194238
</ParamField>
195239

196240
### Open Graph
197241

198242
Controls how this page appears when shared on LinkedIn, Slack, Discord, and other platforms that support Open Graph. Keep titles between 50–60 characters and descriptions between 150–160 characters for optimal display.
199243

200-
<ParamField path="og:site_name" type="string" required={false} toc={true}>
201-
The name of your website as it should appear when your content is shared.
244+
<ParamField path="og:site_name" type="string" required={false} default="metadata.og:site_name" toc={true}>
245+
The name of your website as it should appear when your content is shared. Falls back to the site-wide `metadata.og:site_name` from `docs.yml`.
202246
</ParamField>
203247

204-
<ParamField path="og:title" type="string" required={false} toc={true}>
205-
The title of your page as it should appear when your content is shared.
248+
<ParamField path="og:title" type="string" required={false} default="Page title" toc={true}>
249+
The title of your page as it should appear when your content is shared. Falls back to the page's `title` when unset.
206250
</ParamField>
207251

208-
<ParamField path="og:description" type="string" required={false} toc={true}>
209-
The description of your page as it should appear when your content is shared.
252+
<ParamField path="og:description" type="string" required={false} default="Page description" toc={true}>
253+
The description of your page as it should appear when your content is shared. Falls back to the page's `description`, `subtitle`, or `excerpt` when unset.
210254
</ParamField>
211255

212-
<ParamField path="og:url" type="string" required={false} toc={true}>
213-
The URL of your page.
256+
<ParamField path="og:url" type="string" required={false} default="Page URL" toc={true}>
257+
The URL of your page. Falls back to the page's resolved URL when unset.
214258
</ParamField>
215259

216-
<ParamField path="og:image" type="string" required={false} toc={true}>
217-
The URL of the image displayed when your content is shared.
260+
<ParamField path="og:image" type="string" required={false} default="metadata.og:image" toc={true}>
261+
The URL of the image displayed when your content is shared. Falls back to the site-wide `metadata.og:image` from `docs.yml`.
218262
</ParamField>
219263

220264
<ParamField path="og:image:width" type="number" required={false} toc={true}>
221-
The width of the image in pixels.
265+
The width of the image in pixels. No default; only used when `og:image` is set.
222266
</ParamField>
223267

224268
<ParamField path="og:image:height" type="number" required={false} toc={true}>
225-
The height of the image in pixels.
269+
The height of the image in pixels. No default; only used when `og:image` is set.
226270
</ParamField>
227271

228-
<ParamField path="og:locale" type="string" required={false} toc={true}>
229-
The locale of the page, typically in the format `language_TERRITORY` (e.g., `en_US`).
272+
<ParamField path="og:locale" type="string" required={false} default="metadata.og:locale" toc={true}>
273+
The locale of the page, typically in the format `language_TERRITORY` (e.g., `en_US`). Falls back to the site-wide `metadata.og:locale` from `docs.yml`.
230274
</ParamField>
231275

232-
<ParamField path="og:logo" type="string" required={false} toc={true}>
233-
The URL of your logo image displayed when your content is shared.
276+
<ParamField path="og:logo" type="string" required={false} default="metadata.og:logo" toc={true}>
277+
The URL of your logo image displayed when your content is shared. Falls back to the site-wide `metadata.og:logo` from `docs.yml`.
234278
</ParamField>
235279

236280
### Twitter / X
237281

238282
Controls how this page appears in Twitter Card previews when shared on X.
239283

240-
<ParamField path="twitter:title" type="string" required={false} toc={true}>
241-
The title of your page as it should appear in a tweet.
284+
<ParamField path="twitter:title" type="string" required={false} default="og:title" toc={true}>
285+
The title of your page as it should appear in a tweet. Falls back to `og:title` (and then to the page title) when unset.
242286
</ParamField>
243287

244-
<ParamField path="twitter:description" type="string" required={false} toc={true}>
245-
The description of your page as it should appear in a tweet.
288+
<ParamField path="twitter:description" type="string" required={false} default="og:description" toc={true}>
289+
The description of your page as it should appear in a tweet. Falls back to `og:description` (and then to the page description) when unset.
246290
</ParamField>
247291

248-
<ParamField path="twitter:handle" type="string" required={false} toc={true}>
249-
The Twitter handle of the page creator or site.
292+
<ParamField path="twitter:handle" type="string" required={false} default="metadata.twitter:handle" toc={true}>
293+
The Twitter handle of the page creator or site. Falls back to the site-wide `metadata.twitter:handle` from `docs.yml`.
250294
</ParamField>
251295

252-
<ParamField path="twitter:image" type="string" required={false} toc={true}>
253-
The URL of the image displayed in a tweet.
296+
<ParamField path="twitter:image" type="string" required={false} default="og:image" toc={true}>
297+
The URL of the image displayed in a tweet. Falls back to `og:image` when unset.
254298
</ParamField>
255299

256-
<ParamField path="twitter:site" type="string" required={false} toc={true}>
257-
The Twitter handle for your website.
300+
<ParamField path="twitter:site" type="string" required={false} default="metadata.twitter:site" toc={true}>
301+
The Twitter handle for your website. Falls back to the site-wide `metadata.twitter:site` from `docs.yml`.
258302
</ParamField>
259303

260-
<ParamField path="twitter:url" type="string" required={false} toc={true}>
261-
The URL of your page.
304+
<ParamField path="twitter:url" type="string" required={false} default="og:url" toc={true}>
305+
The URL of your page. Falls back to `og:url` (and then to the page URL) when unset.
262306
</ParamField>
263307

264-
<ParamField path="twitter:card" type="string" required={false} toc={true}>
308+
<ParamField path="twitter:card" type="string" required={false} default="summary_large_image" toc={true}>
265309
The type of card used for sharing on Twitter. Options: `summary`, `summary_large_image`, `app`, `player`.
266310
</ParamField>
267311

0 commit comments

Comments
 (0)