Skip to content

ignacio-nacho-barbano/loveUrl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

18 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

loveUrl

A URL builder and parser pack for handling URL states the easy way.


πŸ“¦ Installation

Using npm:

npm install love-url

Using yarn:

yarn add love-url

Using pnpm:

pnpm add love-url

⚑ TL;DR β€” Quick Usage

type MyParams = {
  showDetails: boolean;
  page: number;
  tags: string[];
  status: "draft" | "published" | "archived";
};

// Encode to URL
// existing params PRESERVED
// currentPath PRESERVED
const url = loveUrl<MyParams>({
  showDetails: true,
  page: 2,
  tags: ["food", "travel"],
  status: "published",
  // This param will be removed
  car: undefined,
});

// Pre-existing params ?name=bob&car=fiat%20multipla&page=1
/*
  url = "/current-path?name=bob&showDetails=true&page=2&tags=_._food_._travel&status=published"
*/

// Encode to URL (existing params REMOVED)
const url = loveUrl<MyParams>(
  {
    showDetails: true,
    page: 2,
    tags: ["food", "travel"],
    status: "published",
  },
  {
    currentParams: null,
    url: "/route-somewhere-else",
  }
);

// Pre-existing params ?name=bob&page=1
/*
  url = "/route-somewhere-else?showDetails=true&page=2&tags=_._food_._travel&status=published"
*/

// Decode from URL
const parsed = parseLoveUrl<MyParams>(
  "?showDetails=true&page=2&tags=_._food_._travel&status=published"
  // or window.location.search
);

/*
  parsed = {
    showDetails: true,
    page: 2,
    tags: ["food", "travel"],
    status: "published"
  }
*/

🧠 Tip

  • Arrays are encoded using a custom separator: _._
    This ensures:
    • Consistent decoding even with a single item (tags=_._design β†’ ["design"])
    • Reliable parsing when array items contain commas
  • Booleans and numbers are parsed and typed automatically
  • Invalid values (e.g., status=broken) will be parsed as strings unless custom validation is added

πŸš€ Motivation

loveUrl aims to simplify the common headaches when building and parsing URLs with parameters in modern JavaScript projects.


πŸ”§ Key Use Cases

1. Building dynamic URLs that preserve client-side state

Imagine your user is at /currentPath/example and the current URL params are:

fileType=photo&selectedPeriod=weekly

Instead of manually constructing links, you can just use:

const ExampleComponent = () => (
  <div>
    {/* Renders: /currentPath/example?fileType=photo&selectedOption=option1&selectedPeriod=weekly */}
    <a href={loveUrl({ selectedOption: "option1" })}>Option 1</a>

    {/* Renders: /currentPath/example?fileType=photo&selectedOption=option2&selectedPeriod=weekly */}
    <a href={loveUrl({ selectedOption: "option2" })}>Option 2</a>

    {/* Renders: /currentPath/example?fileType=photo&selectedPeriod=weekly */}
    <a href={loveUrl({ selectedOption: undefined })}>Clear</a>
  </div>
);

2. Building and fetching dynamic URLs for APIs

Building parameterized API URLs manually can get messy:

type DynamicDataOptions = {
  startDate?: string;
  endDate?: string;
  format?: "photos" | "videos";
  limit: number;
};

const fetchDynamicData = (params: DynamicDataOptions) => {
  let dataUrl = "https://api.my-project/files/archive";

  // Prevent undefined values from appearing in the query string
  for (const key in params) {
    if (params[key] === undefined) {
      delete params[key];
    }
  }

  const queryString = new URLSearchParams(params).toString();
  if (queryString) dataUrl += `?${queryString}`;

  const req = await fetch(dataUrl);
  return await req.json();
};

Or even worse:

const fetchDynamicData = ({
  startDate,
  endDate,
  format,
  limit,
}: DynamicDataOptions) => {
  const req = await fetch(
    `https://api.my-project/files/archive?startDate=${startDate}&endDate=${endDate}&format=${format}&limit=${limit}`
  );

  return await req.json();
};

Imagine doing this with 12+ parameters 😭


βœ… With loveUrl:

const fetchDynamicData = (params: DynamicDataOptions) => {
  const req = await fetch(
    loveUrl(params, { url: "https://api.my-project/files/archive" })
  );

  return await req.json();
};

Clean. Type-safe. No string interpolation errors.


3. Parsing URL params without the chaos

Handling conversions manually can quickly get out of hand:

const ComponentThatUsesParams = () => {
  const params = new URLSearchParams(window.location.search);

  const myBoolean = params.get("myBoolean") === "true"; // string 'false' is truthy 😬
  const limit = parseInt(params.get("limit"));
  const percentage = parseFloat(params.get("percentage"));
  let favoriteFoods = params.get("favoriteFoods");

  if (favoriteFoods?.includes(",")) {
    favoriteFoods = favoriteFoods.split(",");
  }

  return (
    <div>
      <h1>My Params as an object:</h1>
      <code>
        {JSON.stringify({
          myBoolean,
          limit,
          percentage,
        })}
      </code>
    </div>
  );
};

βœ… With loveUrl:

const ComponentThatUsesParams = () => {
  const { myBoolean, limit, percentage, favoriteFoods } = parseLoveUrl<{
    myBoolean: boolean;
    limit: number;
    percentage: number;
    favoriteFoods: string[];
  }>(window.location.search);

  return (
    <div>
      <h1>My Params as an object:</h1>
      <code>
        {JSON.stringify({
          myBoolean,
          limit,
          percentage,
        })}
      </code>
    </div>
  );
};

Type-safe, clean, and handles all conversions for you.


❀️ Why you'll love loveUrl

  • Automatically ignores undefined values
  • Handles complex param types like arrays, numbers, and booleans
  • Uses a custom array separator (_._) for full consistency
  • Prevents messy string interpolation
  • Decodes and encodes with full TypeScript support
  • Deals with duplicate params gracefully
  • Keeps your URLs and logic clean and reliable

Releases

No releases published

Packages

 
 
 

Contributors