diff --git a/src/app/(main)/projects/components/project-card-effect.tsx b/src/app/(main)/projects/components/project-card-effect.tsx
new file mode 100644
index 00000000..0725f78a
--- /dev/null
+++ b/src/app/(main)/projects/components/project-card-effect.tsx
@@ -0,0 +1,29 @@
+'use client'
+
+import { ReactNode } from 'react'
+import Tilt from 'react-parallax-tilt'
+
+export function ProjectCardEffect({
+ children,
+ className
+}: {
+ children: ReactNode
+ className?: string
+}) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/(main)/projects/components/project-card.tsx b/src/app/(main)/projects/components/project-card.tsx
new file mode 100644
index 00000000..5e3a4019
--- /dev/null
+++ b/src/app/(main)/projects/components/project-card.tsx
@@ -0,0 +1,106 @@
+import Image from 'next/image'
+import { Project } from 'contentlayer/generated'
+import { FiFolder, FiGithub, FiGlobe } from 'react-icons/fi'
+import { BsDot } from 'react-icons/bs'
+import { techIcons } from './tech-icons'
+import { ProjectCardEffect } from './project-card-effect'
+
+interface Props {
+ data: Project
+}
+
+export function ProjectCard({ data }: Props) {
+ const Title = () => (
+
+
{data.title}
+
+
+
+
+ {data.core_techs.map(coreTech => {
+ const TechIcon = techIcons[coreTech]
+ return (
+
+
+
+ )
+ })}
+
+
+ )
+ const Tags = () => (
+
+ {data.tags.map(tag => (
+
+ {tag}
+
+ ))}
+
+ )
+
+ return (
+
+
+ {data.image && (
+
+
+
+ )}
+
+
+
+
{data.description}
+
+
+
+
+
+
+ )
+}
diff --git a/src/app/(main)/projects/components/tech-icons.tsx b/src/app/(main)/projects/components/tech-icons.tsx
new file mode 100644
index 00000000..3e933a9e
--- /dev/null
+++ b/src/app/(main)/projects/components/tech-icons.tsx
@@ -0,0 +1,38 @@
+import {
+ SiTypescript,
+ SiReact,
+ SiExpo,
+ SiNextdotjs,
+ SiTailwindcss,
+ SiVite,
+ SiNodedotjs,
+ SiPrisma,
+ SiVuedotjs,
+ SiMdx,
+ SiJavascript
+} from 'react-icons/si'
+
+export const techIcons = {
+ javascript: () => (
+
+ ),
+ typescript: () => (
+
+ ),
+ 'react-native': () => (
+
+ ),
+ expo: () => ,
+ reactjs: () => ,
+ nextjs: () => ,
+ tailwindcss: () => (
+
+ ),
+ vite: () => ,
+ nodejs: () => (
+
+ ),
+ prisma: () => ,
+ vue: () => ,
+ mdx: () =>
+}
diff --git a/src/app/(main)/projects/layout.tsx b/src/app/(main)/projects/layout.tsx
new file mode 100644
index 00000000..1b7d637b
--- /dev/null
+++ b/src/app/(main)/projects/layout.tsx
@@ -0,0 +1,14 @@
+import { Metadata } from 'next'
+
+export const metadata: Metadata = {
+ title: 'Projects',
+ description: 'My personal projects and apps'
+}
+
+export default function AboutLayout({
+ children
+}: {
+ children: React.ReactNode
+}) {
+ return {children}
+}
diff --git a/src/app/(main)/projects/page.tsx b/src/app/(main)/projects/page.tsx
new file mode 100644
index 00000000..0ecc2101
--- /dev/null
+++ b/src/app/(main)/projects/page.tsx
@@ -0,0 +1,36 @@
+import { allProjects } from 'contentlayer/generated'
+import { ProjectCard } from './components/project-card'
+
+export default function Page() {
+ const featuredProjects = allProjects
+ .filter(project => project.featured)
+ .sort((a, b) => Number(a.priority) - Number(b.priority))
+ const otherProjectsWithImage = allProjects
+ .filter(project => !project.featured && project.image)
+ .sort((a, b) => Number(a.priority) - Number(b.priority))
+ const otherProjectsWithoutImage = allProjects
+ .filter(project => !project.featured && !project.image)
+ .sort((a, b) => Number(a.priority) - Number(b.priority))
+
+ return (
+
+
+ Projects
+
+
+ {featuredProjects.length > 0 &&
+ featuredProjects.map(project => (
+
+ ))}
+ {otherProjectsWithImage.length > 0 &&
+ otherProjectsWithImage.map(project => (
+
+ ))}
+ {otherProjectsWithoutImage.length > 0 &&
+ otherProjectsWithoutImage.map(project => (
+
+ ))}
+
+
+ )
+}