Skip to content

Social Utilities & Links

Navigation: HomeFeatures & Components → Social Utilities

Table of Contents

Introduction

The Social Utilities provide reusable components for social media links, email interactions, and copyright notices. These components are designed for flexibility, accessibility, and consistent branding across the portfolio.

Components

  • SocialLink: Flexible social media link component
  • EmailCopyButton: Copy email to clipboard with visual feedback
  • Copyright: Dynamic copyright notice with auto-updating year

Purpose

A versatile link component that supports multiple rendering modes: icon components, image sources, text labels, or custom children.

File: components/social-link.tsx

```typescript:1:66:components/social-link.tsx import Image from "next/image"; import Link from "next/link"; import { ReactNode } from "react"; import { cn } from "@/lib/utils";

interface SocialLinkProps { href: string; ariaLabel: string; className?: string; children?: ReactNode; // Icon component prop (optional) icon?: React.ElementType; // Icon image props (optional) imgSrc?: string; imgAlt?: string; imgWidth?: number; imgHeight?: number; // Text label (optional) label?: string; }

">export default function SocialLink({ href, ariaLabel, className, children, icon: Icon, imgSrc, imgAlt, imgWidth = 32, imgHeight = 32, label, }: SocialLinkProps) { return ( {/ If children are provided, render them directly /} {children || ( <> {/ Render icon if provided /}
      {/* Render image if provided and no icon */}
      {!Icon && imgSrc && (
        <Image
          src={imgSrc}
          alt={imgAlt || ariaLabel}
          width={imgWidth}
          height={imgHeight}
          className="size-6"
        />
      )}

      {label && <span>{label}</span>}

      {!Icon && !imgSrc && !label && ariaLabel}
    </>
  )}
</Link>

); } ```

Rendering Modes

1. Icon Component Mode

typescript import { GitHubIcon, LinkedInIcon } from "@/components/icons"; <SocialLink href="https://github.com/username" ariaLabel="GitHub" icon={GitHubIcon} />

Result: Renders custom SVG icon component

2. Image Source Mode

<SocialLink
  href="https://twitter.com/username"
  ariaLabel="Twitter"
  imgSrc="/images/twitter-icon.png"
  imgAlt="Twitter"
  imgWidth={24}
  imgHeight={24}
/>

Result: Renders Next.js Image component

3. Text Label Mode

<SocialLink
  href="mailto:email@example.com"
  ariaLabel="Email"
  label="Email Me"
/>

Result: Renders text with hover effect

4. Icon + Label Mode

<SocialLink
  href="https://linkedin.com/in/username"
  ariaLabel="LinkedIn"
  icon={LinkedInIcon}
  label="LinkedIn"
/>

Result: Renders both icon and text

5. Custom Children Mode

<SocialLink href="https://example.com" ariaLabel="Custom">
  <div className="flex items-center gap-2">
    <CustomIcon />
    <span>Custom Content</span>
  </div>
</SocialLink>

Result: Renders custom JSX

Props Priority

The component checks props in this order:

{children || (
  <>
    {Icon && <Icon />}
    {!Icon && imgSrc && <Image />}
    {label && <span>{label}</span>}
    {!Icon && !imgSrc && !label && ariaLabel}
  </>
)}
  1. Children (highest priority) - Full custom control
  2. Icon - Component icon
  3. imgSrc - Image fallback
  4. label - Text label
  5. ariaLabel - Fallback text (lowest priority)

Accessibility Features

  • target="_blank" - Opens in new tab
  • rel="noopener noreferrer" - Security best practice
  • aria-label - Screen reader description
  • className="hover:text-primary transition-colors" - Visual feedback

EmailCopyButton Component

Purpose

Provides a button that copies an email address to the clipboard with tooltip feedback.

File: components/email-copy-button.tsx

```typescript:1:37:components/email-copy-button.tsx "use client";

import React from "react"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Copy } from "lucide-react"; import { siteConfig } from "@/lib/config"; import { cn } from "@/lib/utils";

interface EmailCopyButtonProps { className?: string; }

export function EmailCopyButton({ className }: EmailCopyButtonProps) { const [tooltipContent, setTooltipContent] = React.useState("Copy Email");

const handleCopyEmail = () => { navigator.clipboard.writeText(siteConfig.author.email); setTooltipContent("Email Copied!"); setTimeout(() => { setTooltipContent("Copy Email"); }, 2000); // Reset tooltip after 2 seconds };

return (

{tooltipContent}

); }

### User Flow

```mermaid
graph LR
    Hover[Hover Button] --> ShowTooltip[Show 'Copy Email']
    ShowTooltip --> Click[Click Button]
    Click --> CopyClipboard[Copy to Clipboard]
    CopyClipboard --> UpdateTooltip[Update Tooltip: 'Email Copied!']
    UpdateTooltip --> Wait[Wait 2 seconds]
    Wait --> Reset[Reset Tooltip: 'Copy Email']

    style Click fill:#e1f5ff
    style UpdateTooltip fill:#d4edda

Features

  1. Clipboard API: Uses modern navigator.clipboard.writeText()
  2. Visual Feedback: Tooltip changes to "Email Copied!"
  3. Auto-Reset: Returns to "Copy Email" after 2 seconds
  4. Icon Button: Compact design with Copy icon
  5. Configuration: Email sourced from siteConfig.author.email

Browser Compatibility

navigator.clipboard.writeText(email);

Supported:

  • Chrome 63+
  • Firefox 53+
  • Safari 13.1+
  • Edge 79+

Fallback for older browsers:

const handleCopyEmail = () => {
  try {
    if (navigator.clipboard) {
      navigator.clipboard.writeText(siteConfig.author.email);
    } else {
      // Fallback for older browsers
      const textArea = document.createElement("textarea");
      textArea.value = siteConfig.author.email;
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand("copy");
      document.body.removeChild(textArea);
    }
    setTooltipContent("Email Copied!");
  } catch (err) {
    console.error("Failed to copy:", err);
    setTooltipContent("Copy Failed");
  }

  setTimeout(() => {
    setTooltipContent("Copy Email");
  }, 2000);
};

Purpose

Displays a copyright notice with automatically updating year.

File: components/copyright.tsx

```typescript:1:19:components/copyright.tsx "use client"; import React from "react"; import { siteConfig } from "@/lib/config";

interface CopyrightProps { className?: string; }

export default function Copyright({ className }: CopyrightProps) { return (

© {new Date().getFullYear()} {siteConfig.name}. All rights reserved.

); }
### Key Features

1. **Dynamic Year**: `new Date().getFullYear()` always shows current year
2. **Site Config Integration**: Uses `siteConfig.name`
3. **Customizable Styling**: Accept className prop
4. **Client Component**: Must run on client for date

### Output Examples
© 2024 John Doe. All rights reserved. © 2025 Portfolio. All rights reserved. © 2026 Company Name. All rights reserved.
### Why Client Component?

```typescript
"use client"; // Required!

Reason: new Date().getFullYear() runs at render time:

  • Server: Renders year at build time
  • Client: Re-renders with current year

Without "use client", the year would be stale until next build.

Usage Examples

``typescript:108:159:components/layout/footer.tsx <div> <h3 id="contact" className="mb-3 font-semibold"> Connect </h3> <ul> <li> <Button variant="link" className="text-primary-foreground/80 hover:text-primary-foreground w-full justify-start !px-0" asChild > <SocialLink href={siteConfig.social.linkedin} ariaLabel="LinkedIn" label="LinkedIn" icon={LinkedInIcon} /> </Button> </li> <li> <Button variant="link" className="text-primary-foreground/80 hover:text-primary-foreground w-full justify-start !px-0" asChild > <SocialLink href={siteConfig.social.github} ariaLabel="Github" label="Github" icon={GitHubIcon} /> </Button> </li> <li className="flex items-center"> <Button variant="link" className="text-primary-foreground/80 hover:text-primary-foreground grow justify-start !px-0" asChild > <SocialLink href={mailto:${siteConfig.author.email}`} ariaLabel="Email" label="Email" icon={MailIcon} />

  • ### Icon-Only Social Bar
    
    ```typescript
    <div className="flex gap-4">
      <SocialLink
        href="https://github.com/username"
        ariaLabel="GitHub"
        icon={GitHubIcon}
      />
      <SocialLink
        href="https://linkedin.com/in/username"
        ariaLabel="LinkedIn"
        icon={LinkedInIcon}
      />
      <SocialLink
        href="https://twitter.com/username"
        ariaLabel="Twitter"
        imgSrc="/images/twitter.png"
      />
    </div>
    

    Email with Copy Button

    <div className="flex items-center gap-2">
      <SocialLink
        href="mailto:contact@example.com"
        ariaLabel="Email"
        label="contact@example.com"
      />
      <EmailCopyButton />
    </div>
    
    <SocialLink href="https://custom.com" ariaLabel="Custom Platform">
      <div className="flex items-center gap-2 rounded-lg border px-4 py-2">
        <CustomIcon className="h-5 w-5" />
        <span>Follow on Custom</span>
      </div>
    </SocialLink>
    

    typescript:163:163:components/layout/footer.tsx <Copyright className="text-primary-foreground/80 mx-auto py-6 text-center text-sm md:text-base" />

    See Also

    Next Steps

    1. Share Buttons: Add social share buttons for projects
    2. QR Codes: Generate QR codes for contact info
    3. vCard Export: Download contact as vCard file
    4. Social Meta Tags: Add Open Graph and Twitter Card meta tags
    5. Analytics: Track social link clicks
    6. Hover Cards: Show profile previews on hover

    Last Updated: 2024-02-09
    Related Docs: Components | Layout | Icons