Better Blog

React

React SPA client-side rendering with React DOM Router

Render all blog routes under /posts/* using client-side data fetching.

1) main.tsx

import { createRoot } from 'react-dom/client';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Provider } from './providers';
import BlogEntryPage from './BlogEntryPage';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <BrowserRouter>
    <Provider>
      <Routes>
        <Route path="/posts/*" element={<BlogEntryPage />} />
      </Routes>
    </Provider>
  </BrowserRouter>
)

2) BlogEntryPage.tsx

import { useLocation } from "react-router-dom"
import { BlogMetaTags, BlogPageRouter } from "better-blog/client"
import { useBlogDataProvider } from "./useBlogDataProvider"

export default function BlogEntryPage() {
  const location = useLocation()
  const dataProvider = useBlogDataProvider()
  return (
    <main>
      {dataProvider && (
        <BlogMetaTags path={location.pathname} provider={dataProvider} />
      )}
      <BlogPageRouter path={location.pathname} />
    </main>
  )
}

3) providers.tsx

import { QueryClientProvider } from "@tanstack/react-query";
import type { ReactNode } from "react";
import { NavLink, useNavigate } from "react-router-dom"
import { type BlogUIComponents, BlogProvider } from "better-blog/context";
import type { BlogDataProvider } from "better-blog";
import { getOrCreateQueryClient } from "better-blog/queries";
import { ThemeProvider } from "./theme-provider";


// React router DOM link and simple image
const components: BlogUIComponents = {
  Link: ({ href, children, className }) => (
    <NavLink to={href.replace('/posts', '/blog')} className={className}>
      {children}
    </NavLink>
  ),
  Image: ({ src, alt, className }) => (
    <img src={src} alt={alt} className={className} />
  ),
};

// Implement your data provider here
const clientBlogConfig: BlogDataProvider = {
  ...
};

export function Provider({ children }: { children: ReactNode }) {
  const queryClient = getOrCreateQueryClient();
  const navigate = useNavigate()

  return (
    <QueryClientProvider client={queryClient}>
      <ThemeProvider>
        <BlogProvider
          localization={{
            BLOG_LIST_TITLE: "Blog Posts",
          }}
          dataProvider={clientBlogConfig}
          components={components}
          navigate={navigate}
          basePath="/blog"
          adminPermissions={{
            canCreate:true,
            canUpdate:true,
            canDelete:true
          }}
          uploadImage={async (file) => {
            console.log("uploadImage", file);
            // implement your own image upload logic here
            return "https://via.placeholder.com/150/png";
          }}
        >
          {children}
        </BlogProvider>
      </ThemeProvider>
    </QueryClientProvider>
  );
}