Convert base64 string to 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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');
}