Recent articles by category
How to retrieve and display recent articles by category using Optimizely Graph.
You can use Optimizely Graph queries to retrieve and display recent articles based on category filters. These examples let you build dynamic content experiences and suggest related articles to your users.
Basic category-based article query
Use this query to fetch the most recent articles from a specific category. This is ideal for showing topic-based content or suggesting related articles.
The following query retrieves up to 10 articles from a specified category and filters for published content, and sorts results by publish date, showing the newest first:
# This query retrieves up to 10 articles from a specified category.
# It filters for published content and sorts results by publish date (newest first).
query GetRecentArticlesByCategory($category: String, $limit: Int = 10) {
ArticlePage(
where: {
Category: { Name: { eq: $category } }
StartPublish: { lte: "2025-09-14T23:59:59Z" }
}
orderBy: { StartPublish: DESC }
limit: $limit
) {
total
items {
Name
RelativePath
TeaserText
MainBody
StartPublish
PageImage {
Url
}
Category {
Name
}
}
}
}Advanced category filtering with time ranges
Use the following query to filter articles by multiple categories within a specific date range, making it ideal for time-sensitive content or analytics:
# This query retrieves articles from selected categories within a defined time range.
# It returns article details and category facets for filtering and analysis.
query GetArticlesByCategoryAndTimeRange(
$categories: [String!],
$startDate: Date!,
$endDate: Date!,
$limit: Int = 20
) {
ArticlePage(
where: {
_and: [
{ StartPublish: { gte: $startDate, lte: $endDate } }
]
}
orderBy: { StartPublish: DESC }
limit: $limit
) {
total
facets {
Category {
Name(filters: $categories) {
name
count
}
}
}
items {
Name
RelativePath
TeaserText
MainBody
StartPublish
PageImage {
Url
}
Category {
Name
Description
}
}
}
}React implementation
Use the following query to filter articles by multiple categories within a specific date range, making it ideal for time-sensitive content or analytics:
// This component fetches and displays articles based on the selected category.
// It includes loading and error states, and lets users to change category and article count.
import { gql, useQuery } from '@apollo/client';
import { useState } from 'react';
const GET_ARTICLES_BY_CATEGORY = gql`
query GetRecentArticlesByCategory($category: String, $limit: Int = 10) {
ArticlePage(
where: {
Category: { Name: { eq: $category } }
StartPublish: { lte: "2025-09-14T23:59:59Z" }
}
orderBy: { StartPublish: DESC }
limit: $limit
) {
total
items {
Name
RelativePath
TeaserText
MainBody
StartPublish
PageImage {
Url
}
Category {
Name
}
}
}
}
`;
interface Article {
Name: string;
RelativePath: string;
TeaserText?: string;
MainBody?: string;
StartPublish: string;
PageImage?: {
Url: string;
};
Category?: {
Name: string;
};
}
export function ArticlesByCategory() {
const [selectedCategory, setSelectedCategory] = useState<string>('technology');
const [limit, setLimit] = useState(10);
const { loading, error, data, refetch } = useQuery(GET_ARTICLES_BY_CATEGORY, {
variables: { category: selectedCategory, limit },
errorPolicy: 'partial'
});
const handleCategoryChange = (category: string) => {
setSelectedCategory(category);
};
if (loading) return (
<div className="loading-state">
<div className="spinner"></div>
<p>Loading articles...</p>
</div>
);
if (error) return (
<div className="error-state">
<h3>Unable to load articles</h3>
<p>{error.message}</p>
<button onClick={() => refetch()}>Try Again</button>
</div>
);
const articles: Article[] = data?.ArticlePage?.items || [];
return (
<div className="articles-by-category">
<div className="filter-controls">
<h2>Articles by Category</h2>
<div className="category-filters">
{['technology', 'innovation', 'development', 'design', 'business'].map(category => (
<label key={category} className="category-filter">
<input
type="radio"
name="category"
value={category}
checked={selectedCategory === category}
onChange={(e) => handleCategoryChange(e.target.value)}
/>
{category}
</label>
))}
</div>
<div className="limit-control">
<label>
Show:
<select value={limit} onChange={(e) => setLimit(Number(e.target.value))}>
<option value={5}>5 articles</option>
<option value={10}>10 articles</option>
<option value={20}>20 articles</option>
</select>
</label>
</div>
<p className="results-count">
Found {data?.ArticlePage?.total || 0} articles
</p>
</div>
<div className="articles-grid">
{articles.map((article) => (
<article key={article.RelativePath} className="article-card">
{article.PageImage && (
<div className="article-image">
<img
src={article.PageImage.Url}
alt={article.Name}
/>
</div>
)}
<div className="article-content">
<h3>
<a href={article.RelativePath}>{article.Name}</a>
</h3>
{article.TeaserText && (
<p className="teaser">{article.TeaserText}</p>
)}
<div className="article-meta">
<time dateTime={article.StartPublish}>
{new Date(article.StartPublish).toLocaleDateString()}
</time>
{article.Category && (
<span className="category">{article.Category.Name}</span>
)}
</div>
</div>
</article>
))}
</div>
</div>
);
}
``Updated 16 days ago
