Skip to content

blog: support Markdown rendering and 404 routes#38

Merged
gapry merged 5 commits intomainfrom
react-markdown
Mar 5, 2026
Merged

blog: support Markdown rendering and 404 routes#38
gapry merged 5 commits intomainfrom
react-markdown

Conversation

@gapry
Copy link
Owner

@gapry gapry commented Mar 5, 2026

No description provided.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the blog's functionality by introducing dynamic Markdown rendering and robust 404 error handling. It refactors the main application component to interpret URL paths as requests for specific Markdown blog posts, fetching and displaying them accordingly. For any invalid or non-existent paths, a user-friendly 404 page is now presented, improving the overall user experience and site navigation.

Highlights

  • Markdown Rendering Integration: The blog now supports rendering Markdown content dynamically using the react-markdown library, allowing for easy creation and display of blog posts.
  • Dynamic Blog Post Loading: A new routing mechanism has been implemented in App.jsx to fetch and display blog posts from .md files based on the URL path, enabling content-driven URLs.
  • 404 Not Found Handling: A dedicated NotFound component and associated logic in App.jsx were introduced to gracefully handle non-existent routes or blog posts, improving user experience.
  • Dependency Updates: The project's bun.lock and package.json files were updated to include react-markdown and its associated transitive dependencies.
Changelog
  • bun.lock
    • Added react-markdown dependency.
    • Added numerous transitive dependencies required by react-markdown (e.g., @types/debug, @types/hast, mdast-util-to-hast, micromark, unified, etc.).
  • package.json
    • Added react-markdown to the project's dependencies.
  • public/posts/2026/Test.md
    • Added a new sample Markdown file to demonstrate the dynamic content loading.
  • src/App.jsx
    • Refactored the main application component to include state management for content and status.
    • Implemented useEffect to parse URL paths, fetch corresponding Markdown files, and update content.
    • Integrated ReactMarkdown for rendering fetched Markdown.
    • Added conditional rendering for loading, success, and 404 states.
    • Removed the static 'Hello World' heading.
  • src/NotFound.jsx
    • Added a new React component to display a custom 404 'Page Not Found' message.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@amazon-q-developer amazon-q-developer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds Markdown rendering and 404 route handling functionality. Two critical logic errors need to be addressed:

  1. Error handling masking: Network failures are incorrectly treated as 404 errors, misleading users
  2. Missing error state: The error status is set but never handled in the render logic, causing blank screens

These issues will cause incorrect user experiences during network failures. Please address these before merging.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment on lines +34 to +40
fetch(`/posts/${year}/${slug}.md`)
.then(res => res.text())
.then(text => {
setContent(text);
setStatus('success');
})
.catch(() => setStatus('404'));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: The fetch error handler catches all errors and displays a 404 page, which will mislead users when network errors or server errors occur. Users experiencing connection issues will see "Page Not Found" instead of an appropriate error message.

Suggested change
fetch(`/posts/${year}/${slug}.md`)
.then(res => res.text())
.then(text => {
setContent(text);
setStatus('success');
})
.catch(() => setStatus('404'));
fetch(`/posts/${year}/${slug}.md`)
.then(res => {
if (!res.ok) {
setStatus('404');
return;
}
return res.text();
})
.then(text => {
if (text) {
setContent(text);
setStatus('success');
}
})
.catch(() => setStatus('error'));

Comment on lines +49 to +50
if (status === 'loading') return <div>Loading...</div>;
if (status === '404') return <NotFound />;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: Missing status condition handler. The code sets status to 'error' for network failures but only checks for 'loading' and '404' states, causing the component to render nothing when network errors occur.

Suggested change
if (status === 'loading') return <div>Loading...</div>;
if (status === '404') return <NotFound />;
if (status === 'loading') return <div>Loading...</div>;
if (status === '404') return <NotFound />;
if (status === 'error') return <div>Network error. Please try again later.</div>;

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request successfully adds Markdown rendering for blog posts and a 404 routing mechanism. A security audit confirmed no significant vulnerabilities, noting proper validation of Markdown file paths and safe rendering using react-markdown. However, a high-severity issue was identified in error handling when fetching posts, which could lead to unexpected behavior if a file is not found on the server. Additionally, several suggestions were made to improve code organization and maintainability, such as extracting logic into custom hooks, using a layout component to avoid duplication, and moving inline styles to CSS files.


if (allPostFiles[expectedPath]) {
fetch(`/posts/${year}/${slug}.md`)
.then(res => res.text())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fetch API does not reject on HTTP error statuses (like 404 or 500). This means that if the markdown file is not found on the server, the .then block will still execute on an error page's content. You should check if the response was successful before processing it to ensure you show your custom 404 page correctly.

For example:

.then(res => {
  if (!res.ok) {
    throw new Error(`HTTP error! status: ${res.status}`);
  }
  return res.text();
})

### Code Example
```javascript
console.log("Hello from 2026!");
``` No newline at end of file

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a good practice for text files to end with a newline character. Some tools and systems expect this and may behave unexpectedly without it.

Comment on lines +8 to +60
function App() {
const [content, setContent] = useState('');
const [status, setStatus] = useState('loading');

useEffect(() => {
const params = new URLSearchParams(window.location.search);
const redirectedPath = params.get('p');
const currentPath = redirectedPath || window.location.pathname;

if (redirectedPath) {
window.history.replaceState(null, '', redirectedPath);
}

if (currentPath === '/' || currentPath === '/index.html') {
setContent('# Welcome My Blog');
setStatus('success');
return;
}

const parts = currentPath.replace(/\.html$/, '').split('/').filter(Boolean);
const [year, slug] = parts;

if (year && slug) {
const expectedPath = `/public/posts/${year}/${slug}.md`;

if (allPostFiles[expectedPath]) {
fetch(`/posts/${year}/${slug}.md`)
.then(res => res.text())
.then(text => {
setContent(text);
setStatus('success');
})
.catch(() => setStatus('404'));
} else {
setStatus('404');
}
} else {
setStatus('404');
}
}, []);

if (status === 'loading') return <div>Loading...</div>;
if (status === '404') return <NotFound />;

function App() {
return (
<>
<Analytics />
<h1 className="main-title">Hello World</h1>
<article style={{ padding: '40px', maxWidth: '800px', margin: '0 auto' }}>
<ReactMarkdown>{content}</ReactMarkdown>
</article>
</>
);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This component currently handles routing, data fetching, and rendering. This can make it harder to maintain. Consider these refactors:

  • Extract a custom hook: The logic inside useEffect for routing and fetching post data could be extracted into a custom hook (e.g., usePost()).
  • Create a Layout component: <Analytics /> is included here and also in NotFound.jsx. To avoid duplication, you could create a Layout component that renders <Analytics /> and then the specific page content. This would also ensure analytics are always active, even on the loading screen.

}

if (currentPath === '/' || currentPath === '/index.html') {
setContent('# Welcome My Blog');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better maintainability, it's good practice to avoid hardcoding strings directly in the logic. Consider extracting this welcome message into a constant, e.g., const HOME_PAGE_CONTENT = '# Welcome My Blog'; at the top of the file.

<>
<Analytics />
<h1 className="main-title">Hello World</h1>
<article style={{ padding: '40px', maxWidth: '800px', margin: '0 auto' }}>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using inline styles can make the component harder to maintain and reuse, especially as the application grows. Consider moving these styles to a dedicated CSS file or using CSS Modules for better separation of concerns and reusability.

Comment on lines +7 to +14
<div style={{ textAlign: 'center', padding: '10vh 20px' }}>
<h1 style={{ fontSize: '3rem', color: '#ff4d4f' }}>404</h1>
<h2>Page Not Found</h2>
<p>Sorry, the article or page you are looking for seems to have moved or no longer exists.</p>
<a href="/" style={{ color: '#1890ff', textDecoration: 'none' }}>
← Return to homepage
</a>
</div>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to App.jsx, these inline styles could be extracted to a CSS file or CSS Module. This centralizes styling, making it easier to manage and maintain a consistent look and feel across the application.

</div>
</>
);
} No newline at end of file

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's a good practice for files to end with a newline character. Please add one here.

Suggested change
}
}

@gapry gapry merged commit e588589 into main Mar 5, 2026
1 check passed
@gapry gapry deleted the react-markdown branch March 5, 2026 03:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant