diff --git a/www/app/layout.tsx b/www/app/layout.tsx
index e60f87e0..c5b01b2b 100644
--- a/www/app/layout.tsx
+++ b/www/app/layout.tsx
@@ -4,6 +4,7 @@ import "./globals.css";
import { QueryClientProvider } from "@/providers/CustomQueryClientProvider";
import Script from "next/script";
import { Banner } from "@/components/banner";
+import { Toaster } from "sonner";
const outfit = Outfit({
subsets: ["latin"],
@@ -72,6 +73,7 @@ export default function RootLayout({
className={`${outfit.variable} ${spaceGrotesk.variable} font-outfit`}
>
+
{children}
diff --git a/www/components/ProfileSection.tsx b/www/components/ProfileSection.tsx
index 50c6d797..8cd71d3d 100644
--- a/www/components/ProfileSection.tsx
+++ b/www/components/ProfileSection.tsx
@@ -3,6 +3,7 @@ import { Github, Globe, Linkedin, Twitter, User, BookOpen, Instagram } from "luc
import { ProfileSkeleton } from "@/components/skeletons/profile-skeleton";
import { addUserToSupabase, getUserProfile } from "@/lib/api";
import ClientResumeButton from "@/components/ClientResumeButton";
+import ShareButton from "@/components/ShareButton";
import {
Tooltip,
TooltipContent,
@@ -173,6 +174,7 @@ export async function ProfileSection({
);
})}
+
diff --git a/www/components/ShareButton.tsx b/www/components/ShareButton.tsx
new file mode 100644
index 00000000..38089bd0
--- /dev/null
+++ b/www/components/ShareButton.tsx
@@ -0,0 +1,69 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Share2, Check } from "lucide-react";
+import { toast } from "sonner";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+
+export default function ShareButton({ username }: { username: string }) {
+ const [copied, setCopied] = useState(false);
+
+ const handleShare = async () => {
+ const shareData = {
+ title: `${username}'s Portfolio`,
+ text: `Check out ${username}'s professional developer portfolio on devb.io!`,
+ url: window.location.href,
+ };
+
+ if (navigator.share) {
+ try {
+ await navigator.share(shareData);
+ } catch (err) {
+ console.error("Error sharing:", err);
+ }
+ } else {
+ // Fallback: Copy to clipboard
+ try {
+ await navigator.clipboard.writeText(window.location.href);
+ setCopied(true);
+ toast.success("Link copied to clipboard!", {
+ style: {
+ backgroundColor: "#B9FF66",
+ color: "black",
+ border: "1px solid black",
+ fontWeight: "bold",
+ },
+ });
+ setTimeout(() => setCopied(false), 2000);
+ } catch (err) {
+ console.error("Failed to copy:", err);
+ }
+ }
+ };
+
+ return (
+
+
+
+
+
+ {copied ? "Link Copied!" : "Share Portfolio"}
+
+
+ );
+}
diff --git a/www/package.json b/www/package.json
index 8edbdb1b..2d23f76a 100644
--- a/www/package.json
+++ b/www/package.json
@@ -29,6 +29,7 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-intersection-observer": "^9.15.1",
+ "sonner": "^2.0.7",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
"xml2js": "^0.6.2",
diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml
index 9d2e3975..e8803d3b 100644
--- a/www/pnpm-lock.yaml
+++ b/www/pnpm-lock.yaml
@@ -68,6 +68,9 @@ importers:
react-intersection-observer:
specifier: ^9.15.1
version: 9.15.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
+ sonner: #for snackbar
+ specifier: ^2.0.7
+ version: 2.0.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
tailwind-merge:
specifier: ^3.0.2
version: 3.0.2
@@ -2172,6 +2175,12 @@ packages:
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ sonner@2.0.7:
+ resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -4695,6 +4704,11 @@ snapshots:
dependencies:
is-arrayish: 0.3.2
+ sonner@2.0.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
+ dependencies:
+ react: 19.0.0
+ react-dom: 19.0.0(react@19.0.0)
+
source-map-js@1.2.1: {}
stable-hash@0.0.4: {}