{"id":15412891,"url":"https://github.com/ascorbic/unpic-placeholder","last_synced_at":"2025-04-05T23:09:23.440Z","repository":{"id":106146766,"uuid":"607803016","full_name":"ascorbic/unpic-placeholder","owner":"ascorbic","description":"Pure-CSS image placeholders","archived":false,"fork":false,"pushed_at":"2023-03-06T12:00:46.000Z","size":989,"stargazers_count":169,"open_issues_count":3,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-29T22:06:56.525Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://unpic-placeholder.netlify.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ascorbic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-28T17:53:24.000Z","updated_at":"2025-03-19T04:29:10.000Z","dependencies_parsed_at":null,"dependency_job_id":"9bb25c79-8d32-43e9-9687-643a7e9ca7d2","html_url":"https://github.com/ascorbic/unpic-placeholder","commit_stats":{"total_commits":21,"total_committers":2,"mean_commits":10.5,"dds":"0.19047619047619047","last_synced_commit":"1ebffffa1961c4503751c79ae6e44a6b291de0f1"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Funpic-placeholder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Funpic-placeholder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Funpic-placeholder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ascorbic%2Funpic-placeholder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ascorbic","download_url":"https://codeload.github.com/ascorbic/unpic-placeholder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411235,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T16:54:45.333Z","updated_at":"2025-04-05T23:09:23.417Z","avatar_url":"https://github.com/ascorbic.png","language":"TypeScript","readme":"# `@unpic/placeholder` 🖼️\n\nThis is a library for generating low quality image placeholders (LQIP) by\nextracting the dominant color from image, or by server-side rendering a\n[BlurHash](https://blurha.sh) value. These are displayed while an image is\nloading, give better appearance and can help reduce the LCP time. It can render\nthe Blurhash to either a set of CSS gradients, or a tiny BPM image data URI.\nThese are usually around 150 bytes in size, and can be applied as a background\nimage to the img element.\n\nIt works on any modern JavaScript runtime, including Deno, Node and WinterCG\nedge runtimes.\n\n[See the demo](https://unpic-placeholder.netlify.app/)\n\n## Installation\n\n```bash\nnpm install @unpic/placeholder\n```\n\n## Dominant Color\n\nThe library uses the\n[k-means clustering algorithm](https://en.wikipedia.org/wiki/K-means_clustering)\nto extract a dominant color from the image, and can also generate a palette of\ncolors. This function uses the raw pixel data, so it should be used on the\nserver, with access to the image file. The color can then be stored alongside\nthe image path in the database. The library works in the browser, but using this\nas a placeholder would defeat the purpose.\n\n### Usage\n\nNode:\n\n```typescript\nimport { getDominantColor } from \"@unpic/placeholder\";\nimport { getPixels } from \"@unpic/pixels\";\nimport { promises as fs } from \"node:fs\";\n\nexport async function getDominantColorFromImageFile(filePath: string) {\n  // Read the image data from a file\n  const pngData = await fs.readFile(filePath);\n\n  // Decode the image data into raw pixel data\n  const { data } = getPixels(pngData);\n\n  // Get the dominant color\n  return getDominantColor(data);\n}\n```\n\nDeno:\n\n```typescript\nimport { getDominantColor } from \"https://esm.sh/@unpic/placeholder\";\nimport { getPixels } from \"https://deno.land/x/get_pixels/mod.ts\";\n\n// Load the image from the filesystem\nconst pngData = await Deno.readFile(\"image.png\");\n\n// Decode the image data into raw pixel data\nconst { data } = getPixels(pngData);\n\n// Get the dominant color\nconst color = getDominantColor(data);\n```\n\n## BlurHash\n\nA BlurHash is a small string that can be generated from an image and then\nrendered as a placeholder. It should be pre-generated and stored alongside the\nimage URL in the database.\n\nUnlike other BlurHash libraries, this generates CSS values so it works without\nclient-side JavaScript in any web framework or none, and can be displayed before\npage hydration. It was designed for\n[unpic-img](https://github.com/ascorbic/unpic-img), which is a multi-framework\nresponsive image component that generates a single \u003ccode\u003e\u0026lt;img\u0026gt;\u003c/code\u003e tag,\nbut it can be used with `\u003cimg\u003e` tags or with any framework's image component. It\nuses no wrapper elements, and is just applied as a style to the `\u003cimg\u003e` tag.\n\n### Usage\n\nWith `\u003cimg\u003e` tag:\n\n```jsx\nimport { blurhashToImageCssString } from \"@unpic/placeholder\";\n\nconst css = blurhashToImageCssString(blurhash);\nconst img = `\u003cimg src=${src} alt=${alt} style=${css} /\u003e`;\n```\n\nWith `unpic-img`:\n\n```jsx\nimport { blurhashToCssGradientString } from \"@unpic/placeholder\";\nimport { Image } from \"@unpic/react\";\n\nexport function MyImage({ src, alt, blurhash }) {\n  const placeholder = blurhashToCssGradientString(blurhash);\n  return \u003cImage src={src} alt={alt} background={placeholder} /\u003e;\n}\n```\n\n### Generating the BlurHash\n\nYou should pre-generate the BlurHash ahead of time. Some CDNs can do this for\nyou. See [Imgix](https://blog.imgix.com/2021/01/26/blurhash) for example. This\nlibrary does not generate the BlurHash. You can use the\n[blurhash](https://github.com/woltapp/blurhash/tree/master/TypeScript) library\nto do this. There are a few ways to do this. In Node:\n\n```typescript\nimport { encode } from \"blurhash\";\nimport { getPixels } from \"@unpic/pixels\";\n\nconst jpgData = await getPixels(\n  \"https://res.cloudinary.com/demo/image/upload/c_lfill,w_200,h_100/dog.jpg\"\n);\nconst data = Uint8ClampedArray.from(jpgData.data);\nconst blurhash = encode(data, jpgData.width, jpgData.height, 4, 4);\n```\n\nIn Deno:\n\n```typescript\nimport { encode } from \"https://esm.sh/blurhash\";\nimport { getPixels } from \"https://deno.land/x/get_pixels/mod.ts\";\n\nconst jpgData = await getPixels(\n  \"https://res.cloudinary.com/demo/image/upload/c_lfill,w_200,h_100/dog.jpg\"\n);\nconst data = Uint8ClampedArray.from(jpgData.data);\nconst blurhash = encode(data, jpgData.width, jpgData.height, 4, 4);\n```\n\n## API\n\n\u003c!-- TSDOC_START --\u003e\n\n## :toolbox: Functions\n\n- [getPalette](#gear-getpalette)\n- [getDominantColor](#gear-getdominantcolor)\n- [kMeansClusters](#gear-kmeansclusters)\n- [rgbColorToCssString](#gear-rgbcolortocssstring)\n- [blurhashToDataUri](#gear-blurhashtodatauri)\n- [blurhashToCssGradients](#gear-blurhashtocssgradients)\n- [blurhashToCssGradientString](#gear-blurhashtocssgradientstring)\n- [blurhashToGradientCssObject](#gear-blurhashtogradientcssobject)\n- [blurhashToImageCssObject](#gear-blurhashtoimagecssobject)\n- [blurhashToImageCssString](#gear-blurhashtoimagecssstring)\n\n### :gear: getPalette\n\nGets a palette of colors from an image using k-means clustering, sorted in\ndescending order by dominance.\n\n| Function     | Type                                                             |\n| ------------ | ---------------------------------------------------------------- |\n| `getPalette` | `(pixels: Uint8ClampedArray, clusterCount?: number) =\u003e Colour[]` |\n\nParameters:\n\n- `pixels`: The RGBA pixel data of the image.\n- `clusterCount`: The number of colors to return. Defaults to 8.\n\n### :gear: getDominantColor\n\nGets the dominant color in an image. Returns an RGB tuple, e.g. [255, 0, 0] for\nred.\n\n| Function           | Type                                    |\n| ------------------ | --------------------------------------- |\n| `getDominantColor` | `(pixels: Uint8ClampedArray) =\u003e Colour` |\n\nParameters:\n\n- `pixels`: The RGBA pixel data of the image.\n\n### :gear: kMeansClusters\n\nPerforms k-means clustering on an array of pixel data to create a palette of the\nmost common colors.\n\n| Function         | Type                                                                                                        |\n| ---------------- | ----------------------------------------------------------------------------------------------------------- |\n| `kMeansClusters` | `(pixels: Uint8ClampedArray, clusterCount: number, sampleSize: number, maxIterations: number) =\u003e Cluster[]` |\n\nParameters:\n\n- `data`: - The RGBA pixel data to cluster.\n- `clusterCount`: - The number of clusters (i.e., colors) to generate.\n- `sampleSize`: - The number of pixels to randomly sample from the data. Higher\n  numbers take a long time.\n- `maxIterations`: - The maximum number of iterations to perform.\n\n### :gear: rgbColorToCssString\n\nGiven a color as an RGB tuple, returns a CSS string e.g. `rgb(255, 0, 0)`\n\n| Function              | Type                                     |\n| --------------------- | ---------------------------------------- |\n| `rgbColorToCssString` | `([red, green, blue]: Colour) =\u003e string` |\n\n### :gear: blurhashToDataUri\n\nGiven a blurhash, returns a data URI of a BMP image. At tiny sizes, this is\nsmaller than a PNG.\n\n| Function            | Type                                                                                       |\n| ------------------- | ------------------------------------------------------------------------------------------ |\n| `blurhashToDataUri` | `(blurhash: string, width?: number, height?: number) =\u003e `data:image/bmp;base64,${string}`` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `width`: the width of the generated background image. Keep it tiny. Default is\n  8 pixels\n- `height`: the height of the generated background image. Keep it tiny. Default\n  is 8 pixels\n\n### :gear: blurhashToCssGradients\n\nGiven a blurhash, returns an array of CSS linear-gradient() strings. This is a\nrough approximation of the blurhash image but as pure CSS.\n\n| Function                 | Type                                                              |\n| ------------------------ | ----------------------------------------------------------------- |\n| `blurhashToCssGradients` | `(blurhash: string, columns?: number, rows?: number) =\u003e string[]` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `columns`: the number of gradients to generate horizontally. Default is 4\n- `rows`: the number of gradients to generate vertically. Default is 3\n\n### :gear: blurhashToCssGradientString\n\nGiven a blurhash, returns an array of CSS linear-gradient() strings. This is a\nrough approximation of the blurhash image but as pure CSS.\n\n| Function                      | Type                                                            |\n| ----------------------------- | --------------------------------------------------------------- |\n| `blurhashToCssGradientString` | `(blurhash: string, columns?: number, rows?: number) =\u003e string` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `columns`: the number of gradients to generate horizontally. Default is 4\n- `rows`: the number of gradients to generate vertically. Default is 3\n\n### :gear: blurhashToGradientCssObject\n\nGiven a blurhash, returns an object with a CSS background-image property.\n\n| Function                      | Type                                                               |\n| ----------------------------- | ------------------------------------------------------------------ |\n| `blurhashToGradientCssObject` | `(blurhash: string, columns?: number, rows?: number) =\u003e CSSObject` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `columns`: the number of gradients to generate horizontally. Default is 4\n- `rows`: the number of gradients to generate vertically. Default is 3\n\n### :gear: blurhashToImageCssObject\n\nGiven a blurhash, returns an object with CSS background properties to apply to\nan img.\n\n| Function                   | Type                                                               |\n| -------------------------- | ------------------------------------------------------------------ |\n| `blurhashToImageCssObject` | `(blurhash: string, width?: number, height?: number) =\u003e CSSObject` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `width`: the width of the generated background image. Default is 8 pixels\n- `height`: the height of the generated background image. Default is 8 pixels\n\n### :gear: blurhashToImageCssString\n\nGiven a blurhash, returns a CSS string for a background to apply to an img\nelement.\n\n| Function                   | Type                                                            |\n| -------------------------- | --------------------------------------------------------------- |\n| `blurhashToImageCssString` | `(blurhash: string, width?: number, height?: number) =\u003e string` |\n\nParameters:\n\n- `blurhash`: the blurhash string\n- `width`: the width of the generated background image. Default is 8 pixels\n- `height`: the height of the generated background image. Default is 8 pixels\n\n\u003c!-- TSDOC_END --\u003e\n\n---\n\nCopyright © 2023 [Matt Kane](https://github.com/ascorbic). This project is\n[MIT](LICENSE) licensed.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fascorbic%2Funpic-placeholder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fascorbic%2Funpic-placeholder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fascorbic%2Funpic-placeholder/lists"}