
Full stack development feasibility test: building a modern personal blog system
Core Technology Stack
- Next.js 14 + React 18: Leveraging Next.js's powerful SSR capabilities combined with React 18's new features to build a high-performance frontend application.
import { unstable_setRequestLocale } from 'next-intl/server';
import { ScrollIndicator } from '@/components/scroll-indicator';
import { BlogList, getPinnedBlogs } from '@/features/blog';
import { HeroSection } from '@/features/home';
import { ProjectTimeline, getPinnedProjects } from '@/features/project';
export default async function Page({
params: { locale },
}: {
params: { locale: string };
}) {
unstable_setRequestLocale(locale);
const [projectsData, blogsData] = await Promise.all([
getPinnedProjects(locale),
getPinnedBlogs(locale),
]);
const { projects } = projectsData;
const { blogs, uvMap } = blogsData;
return (
<div>
<div className="h-[calc(100vh-64px)] grid place-content-center relative">
<HeroSection />
</div>
<div className="grid place-content-center absolute bottom-8 md:bottom-12 inset-x-0">
<ScrollIndicator />
</div>
<ProjectTimeline projects={projects} />
<BlogList blogs={blogs} uvMap={uvMap} />
</div>
);
}
-
TypeScript: The entire project is developed using TypeScript, providing type safety and a better development experience.
-
Prisma: Simplifies database operations, making CRUD operations more intuitive.
export const getPublishedBlogs = async (locale: string) => {
const blogs = await prisma.blog.findMany({
where: {
published: true,
},
include: {
tags: true,
},
orderBy: {
createdAt: 'desc',
},
});
const localizedBlogs = blogs.map((blog) => ({
...blog,
title: locale === 'zh' ? blog.titleZH : blog.titleEN,
description: locale === 'zh' ? blog.descriptionZH : blog.descriptionEN,
body: locale === 'zh' ? blog.bodyZH : blog.bodyEN,
}));
// ... other code
};
-
Redis + ioredis: Used for tracking article PV/UV data.
-
Tailwind CSS + shadcn/ui: Quickly build beautiful UI interfaces.
plugins: [
require('tailwindcss-animate'),
require('@tailwindcss/typography'),
// Load the plugin that displays the screen size in development mode
process.env.NODE_ENV === 'development' &&
require('tailwindcss-debug-screens'),
// Iconify plugin
addDynamicIconSelectors(),
// Animation plugin
require('tailwindcss-animated'),
require('tailwindcss-textshadow'),
],
} satisfies Config;
Form Handling and Validation
The project uses a combination of shadcn/form, react-hook-form, and zod for form validation, greatly simplifying form handling logic.
export const CreateBlogForm = () => {
// ... other code
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{/* ... other form fields */}
<FormField
control={form.control}
name="tags"
render={({ field }) => (
<FormItem>
<FormLabel>Tag</FormLabel>
<FormControl>
<div className="grid grid-cols-12 gap-4 items-center">
<div className="col-span-10">
<Combobox
options={
tags?.map((el) => ({
label: el.name,
value: el.id,
})) ?? []
}
multiple
clearable
selectPlaceholder="Please choose Tags"
value={field.value}
onValueChange={field.onChange}
/>
</div>
<CreateTagButton refreshAsync={getTagsQuery.refreshAsync} />
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* ... other form fields */}
</form>
</Form>
);
};
Markdown Support
Bytemd is used to implement Markdown writing and preview functionality, providing convenience for content creation.
Theme Switching
Integrated next-theme to support light and dark theme switching, enhancing user experience.
SEO Optimization
Using next-sitemap to automatically generate a site-wide sitemap, beneficial for search engine indexing. Additionally, we utilize dynamic metadata generation to optimize SEO for each page.
export async function generateMetadata({
params,
}: {
params: { slug: string; locale: string };
}): Promise<Metadata> {
const { blog } = await getPlublishedBlogBySlug(params.slug);
if (isNil(blog)) {
return {};
}
const title = params.locale === 'zh' ? blog.titleZH : blog.titleEN;
const description =
params.locale === 'zh' ? blog.descriptionZH : blog.descriptionEN;
return {
title: `${title} - ${WEBSITE}`,
description: description,
keywords: blog.tags.map((el) => el.name).join(','),
};
}
Authentication
Adopting the latest next-auth v5 and siwe to support GitHub account and Ethereum wallet login for the backend, ensuring system security.
Image Processing
After uploading images, sharp is used to compress and convert them to webp format, then uploaded to Alibaba Cloud OSS, optimizing image loading speed.
images: {
remotePatterns: [
{
protocol: 'https',
hostname: '**.aliyuncs.com',
},
// ... other configuration
],
}
Responsive Design
Adapted for different screen sizes to ensure good display across various devices.
export const BlogDetailPage = ({ blog, uv = 0 }: BlogDetailProps) => {
// ... other code
return (
<div className="md:max-w-screen-md 2xl:max-w-6xl md:px-0 md:mx-auto pt-12 md:pt-24 grid gap-9 px-6">
<article className="max-w-[678px] mx-auto overflow-hidden w-full break-all">
{blog.cover && (
<img
src={blog.cover}
alt={title}
className="max-w-screen-md 2xl:max-w-6xl h-auto mb-16 w-full"
/>
)}
<h1 className="mb-4 text-2xl md:text-4xl font-extrabold ">{title}</h1>
{/* ... other content */}
</article>
{/* ... other components */}
</div>
);
};
Backend Management
Integrated complete backend management functionality, including CRUD operations for blog posts, tags, etc.
export const AdminBlogListPage = ({ session }: WithSession) => {
// ... other code
return (
<AdminContentLayout
pageHeader={/* ... */}
>
{/* ... other components */}
<DataTable
columns={columns}
data={data}
total={getBlogsQuery.data?.total}
loading={getBlogsQuery.loading}
params={{ ...params }}
updateParams={updateParams}
noResult={
<div className="grid place-content-center gap-4 py-16">
<IllustrationNoContent />
<p>Empty</p>
<Button onClick={handleGoToCreate}>Create</Button>
</div>
}
/>
</AdminContentLayout>
);
};
Internationalization Support
To make our blog system serve a wider audience, we integrated the next-intl module to implement seamless internationalization functionality.
Application of next-intl
-
Route-level Language Switching:
We utilize Next.js's dynamic routing feature to include the language code as part of the URL. -
Component-level Text Translation:
In components, we use theuseTranslation
hook to retrieve and render translated text.
import { useLocale, useTranslations } from 'next-intl';
export const BlogDetailPage = ({ blog, uv = 0 }: BlogDetailProps) => {
const locale = useLocale();
const t = useTranslations('BlogDetail');
// ...
return (
// ...
<span>
{t('posted', { time: toFromNow(blog.createdAt, locale) })}
</span>
// ...
);
}
-
Localization of Dynamic Content:
For multilingual content stored in the database, we dynamically select the correct language version based on the current language environment. -
SEO Optimization:
We use next-intl to generate localized metadata, enhancing multilingual SEO effectiveness.
Configuration and Integration
To seamlessly integrate next-intl, we made corresponding configurations in next.config.mjs
:
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
// ... other configurations
// Use withNextIntl to wrap configuration
export default withNextIntl(withBundleAnalyzer(config));
Conclusion
Although this is just a personal blog system, it contains many features required by enterprise-level applications. It covers all aspects of full-stack development, from front-end to back-end, from database to cache, from UI design to performance optimization, and internationalization support. Through this project, I not only updated part of the technology stack, but also gained a deeper understanding and verification of more modern web application architecture and development processes.
In the future, I will continue to optimize this project, add more features, and apply the lessons learned to larger projects. At the same time, I will continue to explore more internationalization best practices to ensure that my application can truly serve global users.
For other friends who are learning full-stack development, I suggest that you can also try similar projects. Building a complete application from scratch will give you a more comprehensive understanding of full-stack development. Remember, practice is the best way to learn! At the same time, consider adding internationalization support to your project, which will not only expand the audience of the application, but also provide a more inclusive user experience.