Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions @theme/blog.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import * as React from 'react';

import { Route, Routes as DomRoutes, useParams } from 'react-router-dom';
import { PageLayout } from '@redocly/theme/layouts/PageLayout';
import { useThemeHooks } from '@redocly/theme/core/hooks';

import { SecondaryPostCard } from '@redocly/marketing-pages/components/Blog/SecondaryPostCard.js';

import { Button } from '@redocly/marketing-pages/components/Button/CustomButton.js';
import { CallToAction } from '@redocly/marketing-pages/components/CallToAction/CallToAction.js';
import { FirstThreePosts } from '@redocly/marketing-pages/components/Blog/FirstThreePosts.js';
import { FeaturedClassics } from '@redocly/marketing-pages/components/Blog/FeaturedClassics.js';
import { LatestPosts } from '@redocly/marketing-pages/components/Blog/LatestPosts.js';
import { ContactUs } from '@redocly/marketing-pages/components/Blog/ContactUs.js';
import { Breadcrumbs } from '@redocly/theme/components/Breadcrumbs/Breadcrumbs';
import { BreadcrumbItem } from '@redocly/theme/core/types';
import { H2 } from '@redocly/theme/components/Typography/H2';
import styled from 'styled-components';

export default function BlogRoutes() {
return (
<PageLayout>
<DomRoutes>
<Route path="/" element={<BlogMain />} />
<Route path="/category/:category" element={<CategoryPage />} />
<Route path="/category/:category/:subcategory/" element={<CategoryPage />} />
</DomRoutes>
</PageLayout>
);
}

// Blog main page component
function BlogMain() {
return (
<PageWrapper>
<FirstThreePosts />

<FeaturedClassics />

<LatestPosts />

<ContactUs />

<CallToAction title="Launch API docs you'll be proud of">
<Button
to="https://auth.cloud.redocly.com/registration"
size="large"
className="landing-button"
>
Start 30-day free trial
</Button>
</CallToAction>
</PageWrapper>
);
}

const PageWrapper = styled.div`
position: relative;
overflow: hidden;
`;

// Category page component
function CategoryPage() {
const { category, subcategory } = useParams();
// @ts-ignore
const { usePageSharedData } = useThemeHooks();
const { posts, metadata } = usePageSharedData<any>('blog-posts');

const postList = React.useMemo(() => {
return posts.filter((post) => {
if (!post.categories || post.categories.length === 0) {
return false;
}
return post.categories.some((postCategory) => {
if (
subcategory &&
postCategory.subcategory &&
postCategory.subcategory.id === subcategory &&
postCategory.category.id === category
) {
return true;
} else if (postCategory.category.id === category && !subcategory) {
return true;
}
return false;
});
});
}, [posts, category, subcategory]);

const categoryLabel = React.useMemo(() => {
if (!metadata?.categories) return category;
const categoryData = metadata.categories.find(cat => cat.id === category);
return categoryData?.label || category;
}, [metadata, category]);

const subcategoryLabel = React.useMemo(() => {
if (!subcategory || !metadata?.categories) return subcategory;
const categoryData = metadata.categories.find(cat => cat.id === category);
const subcategoryData = categoryData?.subcategories?.find(sub => sub.id === subcategory);
return subcategoryData?.label || subcategory;
}, [metadata, category, subcategory]);

const breadcrumbItems: BreadcrumbItem[] = subcategory
? [
{ label: 'Blog', link: '/blog' },
{ label: categoryLabel || '', link: `/blog/category/${category}` },
{ label: subcategoryLabel || '', link: `/blog/category/${category}/${subcategory}` },
]
: [
{ label: 'Blog', link: '/blog' },
{ label: categoryLabel || '', link: `/blog/category/${category}` },
];

return (
<CategoryWrapper>
<Breadcrumbs additionalBreadcrumbs={breadcrumbItems} />
<StyledH2>{subcategory ? subcategoryLabel : categoryLabel}</StyledH2>
<CardsWrapper>
{postList.map((post) => (
<SecondaryPostCard
key={post.slug}
title={post.title}
to={`${post.slug}`}
img={post.image}
description={post.description}
publishedDate={post.publishedDate}
author={post.author}
/>
))}
</CardsWrapper>
</CategoryWrapper>
);
}

const StyledH2 = styled(H2)`
font-family: 'Red Hat Display';
font-weight: 700;
font-size: 54px;
line-height: 64px;

margin: 0;

`;

const CardsWrapper = styled.div`
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
`;

const CategoryWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 32px;
margin: 96px auto 160px;
max-width: 1032px;
`;
10 changes: 10 additions & 0 deletions @theme/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export default function themePlugin() {
'preview-template',
fromCurrentDir(import.meta.url, './preview.route.tsx')
);
const blogTemplateId = actions.createTemplate(
'blog-template',
fromCurrentDir(import.meta.url, './blog.page.tsx')
);
actions.addRoute({
excludeFromSidebar: true,
slug: '/preview',
Expand All @@ -47,6 +51,12 @@ export default function themePlugin() {
};
},
});
actions.addRoute({
slug: '/blog/',
fsPath: '/blog/',
templateId: blogTemplateId,
hasClientRoutes: true,
});
},
async afterRoutesCreated(actions, context) {
// Existing blog data processing
Expand Down
39 changes: 36 additions & 3 deletions @theme/utils/blog-post.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,21 @@ export const buildAndSortBlogPosts = async (postRoutes, context, outdir) => {
slug: route.slug,
author: metadata.authors.get(frontmatter.author),
categories: (frontmatter.categories || [])
.map((categoryId) => metadata.categories.get(categoryId))
.map((categoryId) => {
const categoryData = metadata.categories.get(categoryId);
if (!categoryData) return null;

if (categoryData.category && categoryData.subcategory) {
return categoryData;
} else {
return {
category: {
id: categoryData.id,
label: categoryData.label
}
};
}
})
.filter(Boolean),
image:
frontmatter.image &&
Expand All @@ -46,16 +60,35 @@ async function transformMetadata(metadata, cwd, outdir) {
});
}

// Mapping category and subcategory
for (const category of metadata.categories) {
// Store main category as-is (for posts with just main categories)
categories.set(category.id, category);

// Store subcategories with both category and subcategory objects
if (category.subcategories) {
for (const subcategory of category.subcategories) {
const fullId = `${category.id}:${subcategory.id}`;
categories.set(fullId, {
category: {
id: category.id,
label: category.label
},
subcategory: {
id: subcategory.id,
label: subcategory.label
}
});
}
}
}

return { authors, categories };
}

function sortByDatePredicate(a, b) {
const aDate = new Date(a.date);
const bDate = new Date(b.date);
const aDate = new Date(a.publishedDate);
const bDate = new Date(b.publishedDate);

if (aDate.getTime() > bDate.getTime()) {
return -1;
Expand Down
8 changes: 4 additions & 4 deletions blog/accelerated-learning-openapi.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: Learn OpenAPI in a day
description: Accelerated learning approach applied to OpenAPI.
seo:
title: Learn OpenAPI in a day
description: Accelerated learning approach applied to OpenAPI.
author: adam-altman
date: 2023-02-21
publishedDate: "2023-02-21"
categories:
- openapi
- learning
- api-specifications:openapi
- technical-documentation:tutorials-onboarding
image: team.png
---
## Catalyst
Expand Down
9 changes: 4 additions & 5 deletions blog/add-openapi-tags-for-next-level-api-descriptions.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: Add OpenAPI tags for next-level API descriptions
description: Tag your OpenAPI endpoints and help your users navigate their own path to success.
seo:
title: Add OpenAPI tags for next-level API descriptions
description: Tag your OpenAPI endpoints and help your users navigate their own path to success.
author: lorna-mitchell
date: 2024-03-13
publishedDate: "2024-03-13"
categories:
- openapi
- api-design
- api-specifications:openapi
- api-lifecycle:design
image: Redocly_blog_7.jpg
---

Getting to know a new API is always daunting, but for the larger APIs, the hardest part is knowing where to start.
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).

Expand Down
9 changes: 4 additions & 5 deletions blog/all-of-schemas.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: How to reuse schemas when required properties change across endpoints
description: Answering real questions about how to use OpenAPI to design and describe APIs.
seo:
title: How to reuse schemas when required properties change across endpoints
description: Answering real questions about how to use OpenAPI to design and describe APIs.
author: adam-altman
date: 2022-05-04
publishedDate: "2022-05-04"
categories:
- openapi
- api-design
- api-specifications:openapi
- api-lifecycle:design
image: team.png
---

# Extending schemas with `allOf`

Question from C-3PO (yes, names and content are changed to protect the innocent):
Expand Down
5 changes: 2 additions & 3 deletions blog/api-catalog-value.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: The unseen economics of an API catalog
description: Why modern engineering organizations can't operate without one—and how API catalogs deliver measurable ROI through automation, trust, and system visibility.
seo:
title: The unseen economics of an API catalog | Redocly
description: Discover how API catalogs deliver millions in ROI by eliminating search waste, preventing costly mistakes, and enabling faster development through automation and trust.
image: ./images/Redocly_blog_8.jpg
author: adam-altman
date: 2025-11-17
publishedDate: "2025-11-17"
categories:
- api-catalog
image: Redocly_blog_8.jpg
---

# The unseen economics of an API catalog

_Why modern engineering organizations can't operate without one._
Expand Down
7 changes: 3 additions & 4 deletions blog/api-council.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: "API council: the wisdom of your API crowd"
description: Invite the stakeholders inside your organization to the table and make your API the best it can be
seo:
title: "API council: the wisdom of your API crowd"
description: Invite the stakeholders inside your organization to the table and make your API the best it can be
author: lorna-mitchell
date: 2024-04-17
publishedDate: "2024-04-17"
categories:
- openapi
- api-specifications:openapi
image: Redocly_blog_8.jpg
---

API governance is often focused on the creation of standards, and the implementing of those standards using appropriate tooling.
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.
Like most technology problems, the best solutions lie with the people who are part of the process.
Expand Down
9 changes: 4 additions & 5 deletions blog/api-design-approaches.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: Two approaches to API design - Resources vs Workflows
description: Exploring resource-first and workflow-first approaches to API design, and why thinking about use cases early can lead to better APIs.
seo:
title: "Two approaches to API design - Resources vs Workflows"
title: Two approaches to API design - Resources vs Workflows
description: Exploring resource-first and workflow-first approaches to API design, and why thinking about use cases early can lead to better APIs.
image: ./images/api-design-approaches.png
author: adam-altman
date: 2025-10-28
publishedDate: "2025-10-28"
categories:
- api-first
- api-lifecycle:design
image: api-design-approaches.png
---

When designing a new API, there's a fundamental choice that shapes everything that follows: where do you start?
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?

Expand Down
7 changes: 3 additions & 4 deletions blog/api-dev-portal.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
---
template: '../@theme/templates/BlogPost'
template: ../@theme/templates/BlogPost
title: Hello world, our mission is to perfect the API Developer Portal
description: ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.
seo:
title: Hello world, our mission is to perfect the API Developer Portal
description: ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.
author: adam-altman
date: 2018-05-05
publishedDate: "2018-05-05"
categories:
- dev-portal
- developer-portal
image: blog-1.png
---

ReDoc was born out of frustration with rendering my OpenAPI definitions for API reference docs.

Developer documentation is very important to a developer’s experience.
Expand Down
Loading