Snippets

Convert base64 string to blob

TypeScript Base64 Blob
const convertBase64toBlob = (base64String: string, mimeType: string) => {
  const byteCharacters = atob(base64String);
  const byteArrays = [];
  const sliceSize = 512;

  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);
    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }
    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }
  return new Blob(byteArrays, { type: mimeType });
};

Merge classNames helper

React CSS clsx

Simple version

export const cn = (...classes: (boolean | string)[]) =>
  classes.filter(Boolean).join(' ');

Advanced version with twMerge

import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...classes: ClassValue[]) {
  return twMerge(clsx(classes));
}

Convert to webp

Bash webp

Shell script to convert all files in directory to webp, with default params, or standard cwebp params passed from command.

#!/bin/bash

PARAMS=('-m 6 -q 70 -mt -af -progress')

if [ $# -ne 0 ]; then
	PARAMS=$@;
fi

cd $(pwd)

shopt -s globstar nullglob nocaseglob extglob

for FILE in $PWD/**/*.@(jpg|jpeg|tif|tiff|png); do
    cwebp $PARAMS "$FILE" -o "${FILE%.*}".webp;
done

To fix problems with globstar(especially on MacOS), use this gist.

Copy text to clipboard

TypeScript Clipboard
const copyToClipboard = async (textToCopy: string) => {
  if (!navigator.clipboard)
    throw Error('Clipboard API not supported in current browser');
  if (!window.isSecureContext) throw Error('Can only copy in secure contexts');

  await navigator.clipboard.writeText(textToCopy);
};

Fallback Copy (Deprecated)

const fallbackCopyToClipboard = (textToCopy: string) => {
  // Create textarea in Document, focus and select
  let textArea = document.createElement('textarea');
  textArea.value = textToCopy;
  textArea.style.position = 'fixed';
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    let successful = document.execCommand('copy');
    if (!successful) {
      throw new Error('execCommand failed to copy to clipboard');
    }
  } catch (err) {
    throw new Error(`execCommand failed: ${err}`);
  } finally {
    // Remove the created Textarea
    document.body.removeChild(textArea);
  }
};

Get formatted date for a timezone

TypeScript Intl Date Timezone

Convert a timestamp in millis to a particular timezone using JavaScript’s Intl.

const getDateForTimeZone = (
  millis: number,
  tz: string,
  locale = 'en-IN'
): string => {
  const dateFormatter = new Intl.DateTimeFormat(locale, {
    timeZone: tz,
    day: 'numeric',
    month: 'short',
    year: 'numeric',
  });

  const timeFormatter = new Intl.DateTimeFormat(locale, {
    timeZone: tz,
    timeStyle: 'short',
  });

  const timezoneFormatter = new Intl.DateTimeFormat(locale, {
    timeZone: tz,
    timeZoneName: 'long',
  });

  return [
    `${dateFormatter.format(millis)}`,
    `${timeFormatter.format(millis).toUpperCase()}`,
    `${timezoneFormatter.formatToParts(millis).find((p) => p.type === 'timeZoneName')?.value || ''}`,
  ].join(' ');
};

Example usage and outputs:

getDateForTimeZone(1690113193308, 'Asia/Kolkata');
// 23 Jul 2023 5:23 PM India Standard Time

getDateForTimeZone(1690113193308, 'America/Chicago', 'en-US');
// Jul 23, 2023 6:53 AM Central Daylight Time

getDateForTimeZone(1690113193308, 'Europe/Paris', 'fr-FR');
// 23 juil. 2023 13:53 heure d’été d’Europe centrale

Delay

TypeScript Promise Delay

Wait for a period of time in JavaScript.

export const delay = async (millis: number) =>
  new Promise((resolve) => setTimeout(resolve, millis));

Example Usage

// Wait for 2 seconds
await delay(2000);

Bonus: Node.js

If in node, you could also just do

import { setTimeout as delay } from 'node:timers/promises';

// Wait for 2 seconds
await delay(2000);

Display prices in INR

TypeScript Intl Price INR

Display prices with the Rupee symbol, Indian numeric system commas and 2 decimal digits:

const displayInr = (amount: number) =>
  '₹ ' +
  new Intl.NumberFormat('en-IN', {
    maximumFractionDigits: 2,
    minimumFractionDigits: 2,
  }).format(amount);

Example usage and outputs:

displayInr(23);
// "₹ 23.00"

displayInr(1007.5);
// "₹ 1,007.50"

displayInr(1299999.98999);
// "₹ 12,99,999.99"

Get formatted date with ordinals

TypeScript Intl Date Ordinal

Get date formatted with ordinals (st, nd, rd, th, etc) using JavaScript’s Intl.

const getFormattedDate = (dateString: string, locale = 'en-IN') => {
  const ordinalMap: Partial<Record<Intl.LDMLPluralRule, string>> = {
    one: 'st',
    two: 'nd',
    few: 'rd',
    other: 'th',
  };
  const ordinalPluralRules = new Intl.PluralRules(locale, { type: 'ordinal' });

  const formatter = new Intl.DateTimeFormat(locale, {
    day: 'numeric',
    month: 'long',
    year: 'numeric',
  });
  const dateParts = formatter.formatToParts(new Date(dateString));

  // Modify day part by adding ordinal
  const dayPart = dateParts.find((p) => p.type === 'day')!;
  const ordinal = ordinalMap[ordinalPluralRules.select(Number(dayPart.value))];
  dayPart.value = dayPart.value + ordinal;

  return dateParts.map((p) => p.value).join('');
};

Example usage and outputs:

getFormattedDate('03-22-2023');
// "22nd March 2023"

getFormattedDate('08-17-2023', 'en-US');
// "August 17th, 2023"

getFormattedDate('12-31-2023', 'en-GB');
// "31st December 2023"

Limitations

This logic only works for English locales and might not work for others like French, German, etc.

Is in Viewport

TypeScript Browser Viewport

Check if an element is in the browser viewport or not.

const isInViewport = (element: HTMLElement) => {
  const { top, right, bottom, left } = element.getBoundingClientRect();
  return (
    top >= 0 &&
    left >= 0 &&
    bottom <= window.innerHeight &&
    right <= window.innerWidth
  );
};

Keys of Type

TypeScript Types

Extract all the keys of a certain type T from object O.

type KeysOfType<O, T> = {
  [K in keyof O]: O[K] extends T ? K : never;
}[keyof O & string];

Use case

Suppose we have a type User like so:

type User = {
  id: string;
  name: string;
  isAdmin: boolean;
  isEnabled: boolean;
  hasVerifiedEmail: boolean;
};

And we want to extract all the boolean fields from User and put them in a new type called UserFlags. We can do so by:

type UserFlags = KeysOfType<User, boolean>;
//   ^? type UserFlags = "isAdmin" | "isEnabled" | "hasVerifiedEmail"

Pick keys

TypeScript Pick

Pick keys from an object to construct a picked object, with full type support.

const pick = <T, K extends keyof T>(obj: T, keys: K[]) =>
  keys.reduce(
    (pickedObj, currentKey) => {
      pickedObj[currentKey] = obj[currentKey];
      return pickedObj;
    },
    {} as Pick<T, K>
  );

Example Usage

const x = { a: 1, b: 2, c: 3 };
const y = pick(x, ['b']);
// { b: 2 }

Prettify

TypeScript Types

A TypeScript type alias called Prettify. It takes a type as its argument and returns a new type that has the same properties as the original type, but the properties are not intersected. This means that the new type is easier to read and understand.

type Prettify<T> = {
  [K in keyof T]: T[K];
} & {};

For deeply nested types:

type Prettify<T> = {
  [K in keyof T]: T[K] extends object ? Prettify<T[K]> : T[K];
} & {};

Use case

Suppose we have a type that has many intersections:

export type SimpleShape = {
  color: string;
} & {
  size: number;
} & {
  shape: 'circle' | 'square';
};

When we hover over the type SimpleShape with many intersections, it can be difficult to see the resolved type. It would be helpful if there was a way to prettify the display of these types.

With Prettify we can exactly do so, using:

type Shape = Prettify<SimpleShape>;
//   ^? type Shape = {
//        color: string;
//        size: number;
//        shape: "circle" | "square";
//      }

Dynamic GitHub Theme in Shiki

CSS GitHub Theme Shiki Markdown

Dynamically switch between GitHub Light and Dark theme for code highlighting when using Shiki. The following code assumes dark theme will add a dark class on the html tag.

:root {
  /* Light Theme */
  --shiki-color-text: #24292f;
  --shiki-color-background: #f6f8fa;
  --shiki-token-constant: #0550ae;
  --shiki-token-string: #0a3069;
  --shiki-token-comment: #6e7781;
  --shiki-token-keyword: #cf222e;
  --shiki-token-parameter: #116329;
  --shiki-token-function: #8250df;
  --shiki-token-string-expression: #0a3069;
  --shiki-token-punctuation: #24292f;
  --shiki-token-link: #0a3069;
}

.dark {
  /* Dark Theme */
  --shiki-color-text: #c9d1d9;
  --shiki-color-background: #161b22;
  --shiki-token-constant: #79c0ff;
  --shiki-token-string: #a5d6ff;
  --shiki-token-comment: #8b949e;
  --shiki-token-keyword: #ff7b72;
  --shiki-token-parameter: #7ee787;
  --shiki-token-function: #d2a8ff;
  --shiki-token-string-expression: #a5d6ff;
  --shiki-token-punctuation: #c9d1d9;
  --shiki-token-link: #a5d6ff;
}

pre {
  background-color: var(--shiki-color-background);
}

/* Line highlight styles */
code .line.highlighted {
  background-color: #d0d7de60;
  --tw-shadow: 2px 0 #0969da inset;
}

.dark code .line.highlighted {
  background-color: #30363d80;
  --tw-shadow: 2px 0 #58a6ff inset;
}

code .line {
  line-height: 1.75;
}

Stream download a file

TypeScript Node.js Fetch Stream

Download and save a file using streams in Node.js, great for downloading large files without clogging up memory.

import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { finished } from 'node:stream/promises';

export const downloadFile = async (
  url: string,
  fileName: string,
  encoding: string
) => {
  const writeStream = createWriteStream(fileName, { encoding });
  // Use body since it is a readable stream
  console.log('Starting File download...');
  const { body } = await fetch(url);
  if (body) {
    //   Pipe body to the writestream
    await finished(Readable.fromWeb(body as any).pipe(writeStream));
    console.log('File download successful!');
  } else {
    throw new Error('File empty!');
  }
};

Add Inter as a variable font

CSS Font Inter Variable Font

Paste the following code in the global CSS file:

@font-face {
  font-family: 'Inter';
  font-weight: 100 900;
  font-display: swap;
  font-style: normal;
  font-named-instance: 'Regular';
  src: url('/fonts/Inter.var.woff2') format('woff2');
}