@fullstackdatasolutions/articles 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.12.0] - 2026-08-03
9
+
10
+ ### Fixed
11
+
12
+ - **SEO: `ArticlesPage` no longer requires client-side JS to expose article links.** Previously `ArticlesPage` fetched its article list entirely client-side via `useArticles()`, so the server-rendered HTML for `/articles` contained only a "Loading articles..." placeholder with zero `<a href>` links — crawlers that don't execute JS (or that snapshot the initial response) never discovered any article URLs from the index page. `ArticlesPage` now accepts optional `initialArticles`/`initialCategories` props; when a consuming app passes server-fetched data (e.g. via `getAllArticles()`/`getAllCategories()` from `@fullstackdatasolutions/articles/server` in an `async` Server Component), the first render is fully populated with real links and no client fetch occurs on mount. This matches the pattern `CategoryArticlesPage` and `AuthorArticlesPage` already used. Omitting the new props preserves the previous client-fetch behavior, so this is backward compatible.
13
+
8
14
  ## [0.11.0] - 2026-07-30
9
15
 
10
16
  ### Added
package/README.md CHANGED
@@ -110,11 +110,26 @@ export const siteConfig: ArticlesConfig = {
110
110
  // app/articles/page.tsx
111
111
  import type { Metadata } from 'next'
112
112
  import { ArticlesPage } from '@fullstackdatasolutions/articles'
113
- import { generateArticlesIndexMetadata } from '@fullstackdatasolutions/articles/server'
113
+ import {
114
+ generateArticlesIndexMetadata,
115
+ getAllArticles,
116
+ getAllCategories,
117
+ } from '@fullstackdatasolutions/articles/server'
114
118
  import { siteConfig } from '@/config/articles'
115
119
  export const metadata: Metadata = generateArticlesIndexMetadata(siteConfig)
116
- export default function Page() {
117
- return <ArticlesPage config={siteConfig} />
120
+ export default async function Page() {
121
+ // Fetching server-side and passing as initialArticles/initialCategories means the
122
+ // first response already contains real <a href> article links — required for
123
+ // crawlers that don't execute client JS. Omitting these props still works, but
124
+ // falls back to a client-side fetch and a "Loading articles..." placeholder in
125
+ // the initial HTML.
126
+ const [articles, categories] = await Promise.all([
127
+ getAllArticles(siteConfig),
128
+ getAllCategories(),
129
+ ])
130
+ return (
131
+ <ArticlesPage config={siteConfig} initialArticles={articles} initialCategories={categories} />
132
+ )
118
133
  }
119
134
 
120
135
  // app/articles/[...slug]/page.tsx
package/dist/index.cjs CHANGED
@@ -715,12 +715,12 @@ function getBreadcrumbsConfig(config) {
715
715
 
716
716
  // src/useArticles.ts
717
717
  var import_react3 = require("react");
718
- function useArticles() {
718
+ function useArticles(initialArticles, initialCategories) {
719
719
  const [searchQuery, setSearchQuery] = (0, import_react3.useState)("");
720
720
  const debounceRef = (0, import_react3.useRef)(null);
721
- const [articles, setArticles] = (0, import_react3.useState)([]);
722
- const [categories, setCategories] = (0, import_react3.useState)([]);
723
- const [loading, setLoading] = (0, import_react3.useState)(true);
721
+ const [articles, setArticles] = (0, import_react3.useState)(initialArticles != null ? initialArticles : []);
722
+ const [categories, setCategories] = (0, import_react3.useState)(initialCategories != null ? initialCategories : []);
723
+ const [loading, setLoading] = (0, import_react3.useState)(initialArticles === void 0);
724
724
  const [error, setError] = (0, import_react3.useState)(null);
725
725
  const fetchArticles = (0, import_react3.useCallback)((query) => __async(this, null, function* () {
726
726
  try {
@@ -758,7 +758,7 @@ function useArticles() {
758
758
  }
759
759
  }), []);
760
760
  (0, import_react3.useEffect)(() => {
761
- fetchArticles("");
761
+ if (initialArticles === void 0) fetchArticles("");
762
762
  }, [fetchArticles]);
763
763
  const handleSearch = (0, import_react3.useCallback)(
764
764
  (query) => {
@@ -889,9 +889,13 @@ function renderSection(section, ctx, config) {
889
889
  return null;
890
890
  }
891
891
  }
892
- function ArticlesPage({ config }) {
892
+ function ArticlesPage({
893
+ config,
894
+ initialArticles,
895
+ initialCategories
896
+ }) {
893
897
  var _a, _b, _c;
894
- const state = useArticles();
898
+ const state = useArticles(initialArticles, initialCategories);
895
899
  const layout = (_a = config.layout) != null ? _a : DEFAULT_LAYOUT;
896
900
  const pageSize = (_b = config.pageSize) != null ? _b : DEFAULT_PAGE_SIZE;
897
901
  const categoriesPageSize = (_c = config.categoriesPageSize) != null ? _c : DEFAULT_CATEGORIES_PAGE_SIZE;