Skip to content

Commit 244fe65

Browse files
adamaltmanDaryna-delroman-sainchukRomanHotsiy
authored
blog: categorize the posts and add blog reorg announcement (#184)
* blog: categorize the posts and add blog reorg announcement * feat: add blog category pages (#185) * docs: update support email in examples (#2422) remoteId: rem_01jwbtsc654emb9t7qnf1sejgx remoteUpdateId: grupd_01katxqqx4qsdt3b57abak0m8f branchName: main commitSha: 78c9e0b20ccbf886ce259d5bb12fc5e20d0cb43e commitUrl: Redocly/redocly-cli@78c9e0b * feat: add blog category pages * fix: fixes after changing base branch * fix: fixes after changing base branch --------- Co-authored-by: Roman Sainchuk <albuman32@gmail.com> * fix: temporarly comment broken img * feat: add blog-reorganization image to blog post * Update blog/blog-reorganization.md * chore: rename date property to publishedDate in blog * chore: upgrade marketing-pages version --------- Co-authored-by: Daryna Pastushenko <77724851+Daryna-del@users.noreply.github.com> Co-authored-by: Roman Sainchuk <albuman32@gmail.com> Co-authored-by: Daryna Pastushenko <darynapastushenko1207@gmail.com> Co-authored-by: Roman Hotsiy <roman@redocly.com>
1 parent 9f169bc commit 244fe65

File tree

118 files changed

+943
-638
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+943
-638
lines changed

@theme/blog.page.tsx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import * as React from 'react';
2+
3+
import { Route, Routes as DomRoutes, useParams } from 'react-router-dom';
4+
import { PageLayout } from '@redocly/theme/layouts/PageLayout';
5+
import { useThemeHooks } from '@redocly/theme/core/hooks';
6+
7+
import { SecondaryPostCard } from '@redocly/marketing-pages/components/Blog/SecondaryPostCard.js';
8+
9+
import { Button } from '@redocly/marketing-pages/components/Button/CustomButton.js';
10+
import { CallToAction } from '@redocly/marketing-pages/components/CallToAction/CallToAction.js';
11+
import { FirstThreePosts } from '@redocly/marketing-pages/components/Blog/FirstThreePosts.js';
12+
import { FeaturedClassics } from '@redocly/marketing-pages/components/Blog/FeaturedClassics.js';
13+
import { LatestPosts } from '@redocly/marketing-pages/components/Blog/LatestPosts.js';
14+
import { ContactUs } from '@redocly/marketing-pages/components/Blog/ContactUs.js';
15+
import { Breadcrumbs } from '@redocly/theme/components/Breadcrumbs/Breadcrumbs';
16+
import { BreadcrumbItem } from '@redocly/theme/core/types';
17+
import { H2 } from '@redocly/theme/components/Typography/H2';
18+
import styled from 'styled-components';
19+
20+
export default function BlogRoutes() {
21+
return (
22+
<PageLayout>
23+
<DomRoutes>
24+
<Route path="/" element={<BlogMain />} />
25+
<Route path="/category/:category" element={<CategoryPage />} />
26+
<Route path="/category/:category/:subcategory/" element={<CategoryPage />} />
27+
</DomRoutes>
28+
</PageLayout>
29+
);
30+
}
31+
32+
// Blog main page component
33+
function BlogMain() {
34+
return (
35+
<PageWrapper>
36+
<FirstThreePosts />
37+
38+
<FeaturedClassics />
39+
40+
<LatestPosts />
41+
42+
<ContactUs />
43+
44+
<CallToAction title="Launch API docs you'll be proud of">
45+
<Button
46+
to="https://auth.cloud.redocly.com/registration"
47+
size="large"
48+
className="landing-button"
49+
>
50+
Start 30-day free trial
51+
</Button>
52+
</CallToAction>
53+
</PageWrapper>
54+
);
55+
}
56+
57+
const PageWrapper = styled.div`
58+
position: relative;
59+
overflow: hidden;
60+
`;
61+
62+
// Category page component
63+
function CategoryPage() {
64+
const { category, subcategory } = useParams();
65+
// @ts-ignore
66+
const { usePageSharedData } = useThemeHooks();
67+
const { posts, metadata } = usePageSharedData<any>('blog-posts');
68+
69+
const postList = React.useMemo(() => {
70+
return posts.filter((post) => {
71+
if (!post.categories || post.categories.length === 0) {
72+
return false;
73+
}
74+
return post.categories.some((postCategory) => {
75+
if (
76+
subcategory &&
77+
postCategory.subcategory &&
78+
postCategory.subcategory.id === subcategory &&
79+
postCategory.category.id === category
80+
) {
81+
return true;
82+
} else if (postCategory.category.id === category && !subcategory) {
83+
return true;
84+
}
85+
return false;
86+
});
87+
});
88+
}, [posts, category, subcategory]);
89+
90+
const categoryLabel = React.useMemo(() => {
91+
if (!metadata?.categories) return category;
92+
const categoryData = metadata.categories.find(cat => cat.id === category);
93+
return categoryData?.label || category;
94+
}, [metadata, category]);
95+
96+
const subcategoryLabel = React.useMemo(() => {
97+
if (!subcategory || !metadata?.categories) return subcategory;
98+
const categoryData = metadata.categories.find(cat => cat.id === category);
99+
const subcategoryData = categoryData?.subcategories?.find(sub => sub.id === subcategory);
100+
return subcategoryData?.label || subcategory;
101+
}, [metadata, category, subcategory]);
102+
103+
const breadcrumbItems: BreadcrumbItem[] = subcategory
104+
? [
105+
{ label: 'Blog', link: '/blog' },
106+
{ label: categoryLabel || '', link: `/blog/category/${category}` },
107+
{ label: subcategoryLabel || '', link: `/blog/category/${category}/${subcategory}` },
108+
]
109+
: [
110+
{ label: 'Blog', link: '/blog' },
111+
{ label: categoryLabel || '', link: `/blog/category/${category}` },
112+
];
113+
114+
return (
115+
<CategoryWrapper>
116+
<Breadcrumbs additionalBreadcrumbs={breadcrumbItems} />
117+
<StyledH2>{subcategory ? subcategoryLabel : categoryLabel}</StyledH2>
118+
<CardsWrapper>
119+
{postList.map((post) => (
120+
<SecondaryPostCard
121+
key={post.slug}
122+
title={post.title}
123+
to={`${post.slug}`}
124+
img={post.image}
125+
description={post.description}
126+
publishedDate={post.publishedDate}
127+
author={post.author}
128+
/>
129+
))}
130+
</CardsWrapper>
131+
</CategoryWrapper>
132+
);
133+
}
134+
135+
const StyledH2 = styled(H2)`
136+
font-family: 'Red Hat Display';
137+
font-weight: 700;
138+
font-size: 54px;
139+
line-height: 64px;
140+
141+
margin: 0;
142+
143+
`;
144+
145+
const CardsWrapper = styled.div`
146+
display: grid;
147+
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
148+
gap: 20px;
149+
`;
150+
151+
const CategoryWrapper = styled.div`
152+
display: flex;
153+
flex-direction: column;
154+
gap: 32px;
155+
margin: 96px auto 160px;
156+
max-width: 1032px;
157+
`;

@theme/plugin.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export default function themePlugin() {
2727
'preview-template',
2828
fromCurrentDir(import.meta.url, './preview.route.tsx')
2929
);
30+
const blogTemplateId = actions.createTemplate(
31+
'blog-template',
32+
fromCurrentDir(import.meta.url, './blog.page.tsx')
33+
);
3034
actions.addRoute({
3135
excludeFromSidebar: true,
3236
slug: '/preview',
@@ -47,6 +51,12 @@ export default function themePlugin() {
4751
};
4852
},
4953
});
54+
actions.addRoute({
55+
slug: '/blog/',
56+
fsPath: '/blog/',
57+
templateId: blogTemplateId,
58+
hasClientRoutes: true,
59+
});
5060
},
5161
async afterRoutesCreated(actions, context) {
5262
// Existing blog data processing

@theme/utils/blog-post.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,21 @@ export const buildAndSortBlogPosts = async (postRoutes, context, outdir) => {
2323
slug: route.slug,
2424
author: metadata.authors.get(frontmatter.author),
2525
categories: (frontmatter.categories || [])
26-
.map((categoryId) => metadata.categories.get(categoryId))
26+
.map((categoryId) => {
27+
const categoryData = metadata.categories.get(categoryId);
28+
if (!categoryData) return null;
29+
30+
if (categoryData.category && categoryData.subcategory) {
31+
return categoryData;
32+
} else {
33+
return {
34+
category: {
35+
id: categoryData.id,
36+
label: categoryData.label
37+
}
38+
};
39+
}
40+
})
2741
.filter(Boolean),
2842
image:
2943
frontmatter.image &&
@@ -46,16 +60,35 @@ async function transformMetadata(metadata, cwd, outdir) {
4660
});
4761
}
4862

63+
// Mapping category and subcategory
4964
for (const category of metadata.categories) {
65+
// Store main category as-is (for posts with just main categories)
5066
categories.set(category.id, category);
67+
68+
// Store subcategories with both category and subcategory objects
69+
if (category.subcategories) {
70+
for (const subcategory of category.subcategories) {
71+
const fullId = `${category.id}:${subcategory.id}`;
72+
categories.set(fullId, {
73+
category: {
74+
id: category.id,
75+
label: category.label
76+
},
77+
subcategory: {
78+
id: subcategory.id,
79+
label: subcategory.label
80+
}
81+
});
82+
}
83+
}
5184
}
5285

5386
return { authors, categories };
5487
}
5588

5689
function sortByDatePredicate(a, b) {
57-
const aDate = new Date(a.date);
58-
const bDate = new Date(b.date);
90+
const aDate = new Date(a.publishedDate);
91+
const bDate = new Date(b.publishedDate);
5992

6093
if (aDate.getTime() > bDate.getTime()) {
6194
return -1;

blog/accelerated-learning-openapi.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: Learn OpenAPI in a day
44
description: Accelerated learning approach applied to OpenAPI.
55
seo:
66
title: Learn OpenAPI in a day
77
description: Accelerated learning approach applied to OpenAPI.
88
author: adam-altman
9-
date: 2023-02-21
9+
publishedDate: "2023-02-21"
1010
categories:
11-
- openapi
12-
- learning
11+
- api-specifications:openapi
12+
- technical-documentation:tutorials-onboarding
1313
image: team.png
1414
---
1515
## Catalyst

blog/add-openapi-tags-for-next-level-api-descriptions.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: Add OpenAPI tags for next-level API descriptions
44
description: Tag your OpenAPI endpoints and help your users navigate their own path to success.
55
seo:
66
title: Add OpenAPI tags for next-level API descriptions
77
description: Tag your OpenAPI endpoints and help your users navigate their own path to success.
88
author: lorna-mitchell
9-
date: 2024-03-13
9+
publishedDate: "2024-03-13"
1010
categories:
11-
- openapi
12-
- api-design
11+
- api-specifications:openapi
12+
- api-lifecycle:design
1313
image: Redocly_blog_7.jpg
1414
---
15-
1615
Getting to know a new API is always daunting, but for the larger APIs, the hardest part is knowing where to start.
1716
Help users find their way with your API by adding tags to your [OpenAPI](https://www.openapis.org/) [endpoints](https://blog.hubspot.com/website/api-endpoint).
1817

blog/all-of-schemas.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: How to reuse schemas when required properties change across endpoints
44
description: Answering real questions about how to use OpenAPI to design and describe APIs.
55
seo:
66
title: How to reuse schemas when required properties change across endpoints
77
description: Answering real questions about how to use OpenAPI to design and describe APIs.
88
author: adam-altman
9-
date: 2022-05-04
9+
publishedDate: "2022-05-04"
1010
categories:
11-
- openapi
12-
- api-design
11+
- api-specifications:openapi
12+
- api-lifecycle:design
1313
image: team.png
1414
---
15-
1615
# Extending schemas with `allOf`
1716

1817
Question from C-3PO (yes, names and content are changed to protect the innocent):

blog/api-catalog-value.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: The unseen economics of an API catalog
44
description: Why modern engineering organizations can't operate without one—and how API catalogs deliver measurable ROI through automation, trust, and system visibility.
55
seo:
66
title: The unseen economics of an API catalog | Redocly
77
description: Discover how API catalogs deliver millions in ROI by eliminating search waste, preventing costly mistakes, and enabling faster development through automation and trust.
88
image: ./images/Redocly_blog_8.jpg
99
author: adam-altman
10-
date: 2025-11-17
10+
publishedDate: "2025-11-17"
1111
categories:
1212
- api-catalog
1313
image: Redocly_blog_8.jpg
1414
---
15-
1615
# The unseen economics of an API catalog
1716

1817
_Why modern engineering organizations can't operate without one._

blog/api-council.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: "API council: the wisdom of your API crowd"
44
description: Invite the stakeholders inside your organization to the table and make your API the best it can be
55
seo:
66
title: "API council: the wisdom of your API crowd"
77
description: Invite the stakeholders inside your organization to the table and make your API the best it can be
88
author: lorna-mitchell
9-
date: 2024-04-17
9+
publishedDate: "2024-04-17"
1010
categories:
11-
- openapi
11+
- api-specifications:openapi
1212
image: Redocly_blog_8.jpg
1313
---
14-
1514
API governance is often focused on the creation of standards, and the implementing of those standards using appropriate tooling.
1615
At Redocly, we love API tools so much that that we've built a whole company around building them, but successful API practice is about more than tools.
1716
Like most technology problems, the best solutions lie with the people who are part of the process.

blog/api-design-approaches.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: Two approaches to API design - Resources vs Workflows
44
description: Exploring resource-first and workflow-first approaches to API design, and why thinking about use cases early can lead to better APIs.
55
seo:
6-
title: "Two approaches to API design - Resources vs Workflows"
6+
title: Two approaches to API design - Resources vs Workflows
77
description: Exploring resource-first and workflow-first approaches to API design, and why thinking about use cases early can lead to better APIs.
88
image: ./images/api-design-approaches.png
99
author: adam-altman
10-
date: 2025-10-28
10+
publishedDate: "2025-10-28"
1111
categories:
12-
- api-first
12+
- api-lifecycle:design
1313
image: api-design-approaches.png
1414
---
15-
1615
When designing a new API, there's a fundamental choice that shapes everything that follows: where do you start?
1716
Do you begin by modeling your domain with resources and schemas, or do you start by mapping out the workflows and use cases your API needs to support?
1817

blog/api-dev-portal.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
---
2-
template: '../@theme/templates/BlogPost'
2+
template: ../@theme/templates/BlogPost
33
title: Hello world, our mission is to perfect the API Developer Portal
44
description: ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.
55
seo:
66
title: Hello world, our mission is to perfect the API Developer Portal
77
description: ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.
88
author: adam-altman
9-
date: 2018-05-05
9+
publishedDate: "2018-05-05"
1010
categories:
11-
- dev-portal
11+
- developer-portal
1212
image: blog-1.png
1313
---
14-
1514
ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.
1615

1716
Developer documentation is very important to a developer’s experience.

0 commit comments

Comments
 (0)