diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a404de3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +# Make sure it uses up to date node js version +FROM node:23-alpine AS base + +FROM base AS deps +RUN apk add --no-cache libc6-compat +# If you still run into build issue, go to "Problem #3: Making /app is read only. +# in case you have permission issues. +WORKDIR /app + +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ +RUN \ + if [ -f pnpm-lock.yaml ]; then corepack enable && corepack prepare pnpm@latest --activate && pnpm i --frozen-lockfile --ignore-scripts; \ + elif [ -f yarn.lock ]; then corepack enable && corepack prepare yarn@stable --activate && yarn install --frozen-lockfile --ignore-scripts; \ + elif [ -f package-lock.json ]; then npm ci --ignore-scripts; \ + else echo "Lockfile not found." && exit 1; \ + fi + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED=1 +ENV NEXT_PRIVATE_STANDALONE=true + +RUN \ + if [ -f pnpm-lock.yaml ]; then corepack enable && corepack prepare pnpm@latest --activate && pnpm rebuild -r && pnpm run build; \ + elif [ -f yarn.lock ]; then corepack enable && corepack prepare yarn@stable --activate && yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED=1 +ENV NEXT_PRIVATE_STANDALONE=true +ENV NEXT_CACHE_DIR=/tmp/next/cache + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Prepare a writable cache directory for Next.js at runtime +RUN mkdir -p /tmp/next/cache && chown -R nextjs:nodejs /tmp/next && chmod -R 775 /tmp/next + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] \ No newline at end of file diff --git a/README.md b/README.md index abc338e..2667ebe 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,12 @@ A modern, responsive blog built with Next.js 15, Fumadocs MDX, and Tailwind CSS. ## 🚀 Getting Started ```bash -# Clone the repository -git clone -cd blog-template +# option1: build docker image and run a container +docker build -t ze-portfolio . +docker run --rm -p 3000:3000 ze-portfolio -# Install dependencies +# option2: Run locally pnpm install - -# Start development server pnpm dev # Build for production diff --git a/app/blog/[slug]/metadata.ts b/app/blog/[slug]/metadata.ts index 32deee9..ad1ae6c 100644 --- a/app/blog/[slug]/metadata.ts +++ b/app/blog/[slug]/metadata.ts @@ -1,108 +1,108 @@ -import { Metadata } from "next"; -import { docs, meta } from "@/.source"; -import { loader } from "fumadocs-core/source"; -import { createMDXSource } from "fumadocs-mdx"; -import { siteConfig } from "@/lib/site"; - -const blogSource = loader({ - baseUrl: "/blog", - source: createMDXSource(docs, meta), -}); - -export async function generateMetadata({ - params, -}: { - params: Promise<{ slug: string }>; -}): Promise { - try { - const { slug } = await params; - - if (!slug || slug.length === 0) { - return { - title: "Blog Not Found", - description: "The requested blog post could not be found.", - }; - } - - const page = blogSource.getPage([slug]); - - if (!page) { - return { - title: "Blog Not Found", - description: "The requested blog post could not be found.", - }; - } - - const ogUrl = `${siteConfig.url}/blog/${slug}`; - const ogImage = `${ogUrl}/opengraph-image`; - - return { - title: page.data.title, - description: page.data.description, - keywords: [ - page.data.title, - ...(page.data.tags || []), - "Blog", - "Article", - "Web Development", - "Programming", - "Technology", - "Software Engineering", - ], - authors: [ - { - name: page.data.author || "Magic UI", - url: siteConfig.url, - }, - ], - creator: page.data.author || "Magic UI", - publisher: "Magic UI", - robots: { - index: true, - follow: true, - googleBot: { - index: true, - follow: true, - "max-video-preview": -1, - "max-image-preview": "large", - "max-snippet": -1, - }, - }, - openGraph: { - title: page.data.title, - description: page.data.description, - type: "article", - url: ogUrl, - publishedTime: page.data.date, - authors: [page.data.author || "Magic UI"], - tags: page.data.tags, - images: [ - { - url: page.data.thumbnail || ogImage, - width: 1200, - height: 630, - alt: page.data.title, - }, - ], - siteName: siteConfig.name, - }, - twitter: { - card: "summary_large_image", - title: page.data.title, - description: page.data.description, - images: [page.data.thumbnail || ogImage], - creator: "@dillionverma", - site: "@dillionverma", - }, - alternates: { - canonical: ogUrl, - }, - }; - } catch (error) { - console.error("Error generating metadata:", error); - return { - title: "Blog Not Found", - description: "The requested blog post could not be found.", - }; - } -} +import { Metadata } from "next"; +import { docs, meta } from "@/.source"; +import { loader } from "fumadocs-core/source"; +import { createMDXSource } from "fumadocs-mdx"; +import { siteConfig } from "@/lib/site"; + +const blogSource = loader({ + baseUrl: "/blog", + source: createMDXSource(docs, meta), +}); + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug: string }>; +}): Promise { + try { + const { slug } = await params; + + if (!slug || slug.length === 0) { + return { + title: "Blog Not Found", + description: "The requested blog post could not be found.", + }; + } + + const page = blogSource.getPage([slug]); + + if (!page) { + return { + title: "Blog Not Found", + description: "The requested blog post could not be found.", + }; + } + + const ogUrl = `${siteConfig.url}/blog/${slug}`; + const ogImage = `${ogUrl}/opengraph-image`; + + return { + title: page.data.title, + description: page.data.description, + keywords: [ + page.data.title, + ...(page.data.tags || []), + "Blog", + "Article", + "Web Development", + "Programming", + "Technology", + "Software Engineering", + ], + authors: [ + { + name: page.data.author || "Magic UI", + url: siteConfig.url, + }, + ], + creator: page.data.author || "Magic UI", + publisher: "Magic UI", + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + "max-video-preview": -1, + "max-image-preview": "large", + "max-snippet": -1, + }, + }, + openGraph: { + title: page.data.title, + description: page.data.description, + type: "article", + url: ogUrl, + publishedTime: page.data.date, + authors: [page.data.author || "Magic UI"], + tags: page.data.tags, + images: [ + { + url: page.data.thumbnail || ogImage, + width: 1200, + height: 630, + alt: page.data.title, + }, + ], + siteName: siteConfig.name, + }, + twitter: { + card: "summary_large_image", + title: page.data.title, + description: page.data.description, + images: [page.data.thumbnail || ogImage], + creator: "@dillionverma", + site: "@dillionverma", + }, + alternates: { + canonical: ogUrl, + }, + }; + } catch (error) { + console.error("Error generating metadata:", error); + return { + title: "Blog Not Found", + description: "The requested blog post could not be found.", + }; + } +} diff --git a/app/page.tsx b/app/page.tsx index a2de68b..3c67942 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -89,10 +89,10 @@ export default async function HomePage({

- Magic UI Blog + Portfolio

- Latest news and updates from Magic UI. + All my projects and work.

diff --git a/blog/content/21-best-free-react-components.mdx b/blog/content/21-best-free-react-components.mdx deleted file mode 100644 index f46fa7a..0000000 --- a/blog/content/21-best-free-react-components.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: "21 Best Free React Components Libraries To Kickstart Projects" -description: "Discover the most powerful and popular React component libraries that will accelerate your development workflow and help you build stunning UIs." -date: "2024-12-01" -tags: ["UI Frameworks", "React", "Components"] -featured: true -readTime: "16 min read" -author: "arghya" -thumbnail: "/thumbnails/react-components-libraries.jpg" ---- - -Building modern React applications requires a solid foundation of reusable components. Whether you're starting a new project or looking to enhance an existing one, having access to high-quality component libraries can significantly accelerate your development process. - -## Why Use Component Libraries? - -Component libraries offer several advantages: - -- **Consistency**: Maintain a unified design language across your application -- **Speed**: Rapid prototyping and development -- **Accessibility**: Built-in accessibility features and best practices -- **Customization**: Flexible theming and styling options - -## Top React Component Libraries - -### 1. Material-UI (MUI) - -Material-UI is one of the most popular React component libraries, implementing Google's Material Design principles. - -```tsx -import Button from "@mui/material/Button"; -import Stack from "@mui/material/Stack"; - -export default function BasicButtons() { - return ( - - - - - - ); -} -``` - -**Key Features:** - -- Comprehensive component set -- Built-in dark mode support -- Excellent TypeScript support -- Active community and regular updates - -### 2. Ant Design - -A design language and React components library with a focus on enterprise-class UI design language. - -```tsx -import { Button, Space } from "antd"; - -const App = () => ( - - - - - - - -); -``` - -**Key Features:** - -- Enterprise-focused design -- Comprehensive internationalization -- Advanced data handling components -- Built-in form validation - -### 3. Chakra UI - -A modular and accessible component library that gives you the building blocks to build React applications. - -```tsx -import { Button, ButtonGroup } from "@chakra-ui/react"; - -function Example() { - return ( - - - - - ); -} -``` - -**Key Features:** - -- Simple and modular -- Dark mode support -- Accessibility-first approach -- Easy theming system - -### 4. React Bootstrap - -Bootstrap components built for React, providing familiar Bootstrap styling with React functionality. - -```tsx -import Button from "react-bootstrap/Button"; -import ButtonGroup from "react-bootstrap/ButtonGroup"; - -function BasicExample() { - return ( - - - - - - ); -} -``` - -## Performance Considerations - -When choosing a component library, consider: - -- **Bundle size impact**: Use tree-shaking to reduce bundle size -- **Server-side rendering**: Ensure compatibility with SSR -- **Loading performance**: Lazy load components when possible -- **Accessibility**: Choose libraries with built-in a11y features - -## Getting Started - -1. **Choose your library**: Consider your project needs and design requirements -2. **Install dependencies**: Follow the library's installation guide -3. **Configure theming**: Set up your design tokens and theme -4. **Start building**: Begin with basic components and expand - -## Conclusion - -The React ecosystem offers numerous high-quality component libraries, each with its own strengths. Whether you prefer the comprehensive approach of Material-UI, the enterprise focus of Ant Design, or the simplicity of Chakra UI, there's a solution that fits your project needs. - -Remember to evaluate libraries based on your specific requirements, including design philosophy, bundle size, community support, and long-term maintenance. diff --git a/blog/content/heimlab.mdx b/blog/content/heimlab.mdx new file mode 100644 index 0000000..89514cf --- /dev/null +++ b/blog/content/heimlab.mdx @@ -0,0 +1,12 @@ +--- +title: "Heimlab" +description: "A home server infrastructure using Docker" +date: "2025-10-16" +tags: ["infrastructure", "docker", "frontend", "backend", "postgreSQL"] +featured: true +readTime: "16 min read" +author: "Ferez" +thumbnail: "/thumbnails/thumbnail-heimlab.png" +--- + +## What is it? diff --git a/blog/content/hemewimp.mdx b/blog/content/hemewimp.mdx new file mode 100644 index 0000000..3b0d25f --- /dev/null +++ b/blog/content/hemewimp.mdx @@ -0,0 +1,10 @@ +--- +title: "Help Me With My Prompt" +description: "A very simple app that helps you generate a concise prompt for your LLM of choice" +date: "2025-10-16" +tags: ["frontend", "AI"] +featured: true +readTime: "16 min read" +author: "Ferez" +thumbnail: "/thumbnails/thumbnail-hemewimp.png" +--- diff --git a/blog/content/nextjs-portfolio-templates.mdx b/blog/content/nextjs-portfolio-templates.mdx deleted file mode 100644 index 5506391..0000000 --- a/blog/content/nextjs-portfolio-templates.mdx +++ /dev/null @@ -1,322 +0,0 @@ ---- -title: "23 NextJS Portfolio Template Examples For Design Inspiration" -description: "Explore the best NextJS portfolio templates and examples to showcase your work professionally and stand out from the competition." -date: "2024-11-28" -tags: ["Landing Page Examples"] -featured: true -readTime: "17 min read" -author: "arghya" -thumbnail: "/thumbnails/nextjs-portfolio-templates.jpg" ---- - -Your portfolio is often the first impression potential clients or employers have of your work. In today's competitive landscape, having a well-designed, fast, and responsive portfolio website is crucial for standing out. - - - -## Why Choose NextJS for Your Portfolio? - -NextJS offers several advantages for portfolio websites: - -- **Performance**: Built-in optimization and static generation -- **SEO**: Server-side rendering for better search visibility -- **Developer Experience**: Hot reload, TypeScript support, and modern tooling -- **Flexibility**: Can be deployed anywhere and scales easily - - - - Features - -
    -
  • - Redesigned navigation menu with improved organization - and visual hierarchy -
  • -
  • - Enhanced accessibility features with full keyboard navigation support -
  • -
  • High contrast mode for users with visual impairments
  • -
  • Optimized touch targets for better mobile interaction
  • -
  • Enhanced swipe gestures and mobile-specific navigation
  • -
-
-
- - Bug Fixes - -
    -
  • - Fixed inconsistent button placement across different screen sizes -
  • -
  • Resolved color contrast issues affecting readability
  • -
  • Corrected tab order problems in form navigation
  • -
  • Fixed device orientation change handling on mobile devices
  • -
  • Improved ARIA label compatibility with screen readers
  • -
-
-
-
- -## Essential Portfolio Sections - -### Hero Section - -Your hero section should immediately communicate who you are and what you do. - -```tsx -export default function Hero() { - return ( -
-
-

- Hi, I'm John Doe -

-

- Full-stack developer passionate about creating beautiful, functional - web experiences -

-
- - -
-
-
- ); -} -``` - -### Project Showcase - -Display your best work with engaging visuals and clear descriptions. - -```tsx -interface Project { - id: string; - title: string; - description: string; - image: string; - technologies: string[]; - liveUrl?: string; - githubUrl?: string; -} - -export default function ProjectGrid({ projects }: { projects: Project[] }) { - return ( -
- {projects.map((project) => ( -
- {project.title} -
-
-

{project.title}

-

{project.description}

-
-
-
- ))} -
- ); -} -``` - -## Design Trends for 2024 - -### Minimalist Layouts - -Clean, focused designs that put your work front and center: - -- Generous white space -- Typography-first approach -- Subtle animations and transitions -- Limited color palettes - -### Interactive Elements - -Engage visitors with interactive components: - -```tsx -import { useState } from "react"; - -export default function InteractiveSkillBar() { - const [hoveredSkill, setHoveredSkill] = useState(null); - - const skills = [ - { name: "React", level: 90 }, - { name: "TypeScript", level: 85 }, - { name: "NextJS", level: 88 }, - ]; - - return ( -
- {skills.map((skill) => ( -
setHoveredSkill(skill.name)} - onMouseLeave={() => setHoveredSkill(null)} - className="relative" - > -
- {skill.name} - {skill.level}% -
-
-
-
-
- ))} -
- ); -} -``` - -### Dark Mode Support - -Implement dark mode for better user experience: - -```tsx -"use client"; - -import { useTheme } from "next-themes"; -import { useState, useEffect } from "react"; - -export default function ThemeToggle() { - const [mounted, setMounted] = useState(false); - const { theme, setTheme } = useTheme(); - - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - - return ( - - ); -} -``` - -## Performance Optimization - -### Image Optimization - -Use NextJS Image component for optimal loading: - -```tsx -import Image from "next/image"; - -export default function OptimizedPortfolioImage() { - return ( - Project Screenshot - ); -} -``` - -### Static Generation - -Pre-generate pages for better performance: - -```tsx -export async function getStaticProps() { - const projects = await getProjects(); - - return { - props: { - projects, - }, - revalidate: 3600, // Revalidate every hour - }; -} -``` - -## SEO Best Practices - -### Meta Tags - -```tsx -import Head from "next/head"; - -export default function Portfolio() { - return ( - <> - - John Doe - Full Stack Developer - - - - - - - {/* Your portfolio content */} - - ); -} -``` - -### Structured Data - -Add JSON-LD structured data for better search visibility: - -```tsx -const portfolioSchema = { - "@context": "https://schema.org", - "@type": "Person", - name: "John Doe", - jobTitle: "Full Stack Developer", - url: "https://johndoe.dev", - sameAs: ["https://github.com/johndoe", "https://linkedin.com/in/johndoe"], -}; - -export default function Portfolio() { - return ( - <> -