Skip to main content

Overview

The URL utilities module provides functions for working with URLs in the quick links feature. It handles URL normalization, validation, title extraction, and favicon retrieval.

Regular Expressions

const HTTPS_REGEX = /^https?:\/\//i;
const WWW_REGEX = /^www\./;
Internal patterns for detecting protocols and www prefixes.

Functions

normalizeUrl

function normalizeUrl(url: string): string
Normalizes a URL by ensuring it has a protocol prefix. Parameters:
  • url - The URL string to normalize
Returns: string - Normalized URL with https:// prefix, or empty string if input is empty Behavior:
  • Trims whitespace from input
  • Returns empty string if input is empty/whitespace
  • Adds https:// prefix if no protocol is present
  • Preserves existing http:// or https:// protocols
Example:
import { normalizeUrl } from "@/lib/url-utils";

normalizeUrl("google.com");
// Returns: "https://google.com"

normalizeUrl("http://example.com");
// Returns: "http://example.com"

normalizeUrl("  github.com  ");
// Returns: "https://github.com"

normalizeUrl("");
// Returns: ""

extractTitle

function extractTitle(url: string): string
Extracts a display title from a URL by using the hostname. Parameters:
  • url - The URL to extract a title from
Returns: string - Lowercase hostname without www. prefix, or lowercase URL if parsing fails Behavior:
  • Parses URL to extract hostname
  • Removes www. prefix if present
  • Converts result to lowercase
  • Falls back to lowercase URL if parsing fails
Example:
import { extractTitle } from "@/lib/url-utils";

extractTitle("https://www.github.com/user/repo");
// Returns: "github.com"

extractTitle("https://docs.example.com");
// Returns: "docs.example.com"

extractTitle("not-a-url");
// Returns: "not-a-url"

getFaviconUrl

function getFaviconUrl(url: string): string
Generates a Google favicon service URL for the given URL’s domain. Parameters:
  • url - The URL to get a favicon for
Returns: string - Google favicon service URL, or empty string if URL is invalid Behavior:
  • Extracts hostname from URL
  • Returns Google’s favicon service URL with 64px size
  • Returns empty string if URL parsing fails
Example:
import { getFaviconUrl } from "@/lib/url-utils";

getFaviconUrl("https://github.com");
// Returns: "https://www.google.com/s2/favicons?domain=github.com&sz=64"

function LinkWithIcon({ url }: { url: string }) {
  return (
    <a href={url}>
      <img src={getFaviconUrl(url)} alt="" width={16} height={16} />
      {extractTitle(url)}
    </a>
  );
}

isValidUrl

function isValidUrl(url: string): boolean
Validates whether a string is a valid URL. Parameters:
  • url - The string to validate
Returns: boolean - true if valid URL, false otherwise Behavior:
  • Uses native URL constructor for validation
  • Returns true if URL can be parsed
  • Returns false if URL constructor throws
This function requires a protocol to be present. Use with normalizeUrl to validate user input.
Example:
import { isValidUrl, normalizeUrl } from "@/lib/url-utils";

isValidUrl("https://github.com");
// Returns: true

isValidUrl("github.com");
// Returns: false (no protocol)

isValidUrl("not a url");
// Returns: false

// Proper validation flow:
const userInput = "github.com";
const normalized = normalizeUrl(userInput);
if (isValidUrl(normalized)) {
  console.log("Valid URL:", normalized);
}

Complete Example

Here’s a complete implementation of adding quick links:
import { useState } from "react";
import {
  normalizeUrl,
  extractTitle,
  getFaviconUrl,
  isValidUrl,
} from "@/lib/url-utils";

interface QuickLink {
  id: string;
  title: string;
  url: string;
  favicon: string;
}

function QuickLinks() {
  const [links, setLinks] = useState<QuickLink[]>([]);
  const [newUrl, setNewUrl] = useState("");
  const [error, setError] = useState("");

  const addLink = () => {
    // Normalize URL first
    const normalizedUrl = normalizeUrl(newUrl);
    if (!normalizedUrl) {
      setError("URL cannot be empty");
      return;
    }

    // Validate normalized URL
    if (!isValidUrl(normalizedUrl)) {
      setError("Invalid URL format");
      return;
    }

    // Create link with extracted metadata
    const link: QuickLink = {
      id: crypto.randomUUID(),
      title: extractTitle(normalizedUrl),
      url: normalizedUrl,
      favicon: getFaviconUrl(normalizedUrl),
    };

    setLinks((prev) => [...prev, link]);
    setNewUrl("");
    setError("");
  };

  return (
    <div>
      <div>
        <input
          type="text"
          value={newUrl}
          onChange={(e) => setNewUrl(e.target.value)}
          placeholder="Enter URL (e.g., github.com)"
        />
        <button onClick={addLink}>Add Link</button>
        {error && <div style={{ color: "red" }}>{error}</div>}
      </div>

      <div>
        {links.map((link) => (
          <a key={link.id} href={link.url}>
            <img src={link.favicon} alt="" width={16} height={16} />
            {link.title}
          </a>
        ))}
      </div>
    </div>
  );
}
Here’s how these utilities are used in the actual Quick Links component:
import {
  extractTitle,
  getFaviconUrl,
  isValidUrl,
  normalizeUrl,
} from "@/lib/url-utils";

const addLink = () => {
  const normalizedUrl = normalizeUrl(newUrl);
  if (!normalizedUrl) {
    return;
  }

  if (!isValidUrl(normalizedUrl)) {
    return;
  }

  const link: QuickLink = {
    id: crypto.randomUUID(),
    title: extractTitle(normalizedUrl),
    url: normalizedUrl,
    favicon: getFaviconUrl(normalizedUrl),
  };

  setLinks((prev) => [...prev, link]);
  setNewUrl("");
};

Common Patterns

URL Validation Flow

const validateAndNormalize = (input: string): string | null => {
  const normalized = normalizeUrl(input);
  if (!normalized || !isValidUrl(normalized)) {
    return null;
  }
  return normalized;
};
function LinkPreview({ url }: { url: string }) {
  const normalized = normalizeUrl(url);
  const isValid = isValidUrl(normalized);

  if (!isValid) {
    return <div>Invalid URL</div>;
  }

  return (
    <div>
      <img src={getFaviconUrl(normalized)} alt="" />
      <span>{extractTitle(normalized)}</span>
      <a href={normalized}>Visit</a>
    </div>
  );
}

Build docs developers (and LLMs) love