{"id":13880749,"url":"https://github.com/neugelb/sidepix","last_synced_at":"2025-06-30T16:03:13.703Z","repository":{"id":37579694,"uuid":"501175158","full_name":"neugelb/sidepix","owner":"neugelb","description":"\u003cpicture\u003e component with image processing as side-effect","archived":false,"fork":false,"pushed_at":"2024-09-27T11:47:32.000Z","size":9726,"stargazers_count":43,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-19T06:31:19.629Z","etag":null,"topics":["image","next","optimization","picture","react","react-component","sharp"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/neugelb.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":"2022-06-08T09:03:56.000Z","updated_at":"2024-10-08T12:16:05.000Z","dependencies_parsed_at":"2024-11-19T06:41:09.865Z","dependency_job_id":null,"html_url":"https://github.com/neugelb/sidepix","commit_stats":{"total_commits":22,"total_committers":1,"mean_commits":22.0,"dds":0.0,"last_synced_commit":"b63955922cff0578ee3141013b17bc238fece00d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neugelb%2Fsidepix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neugelb%2Fsidepix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neugelb%2Fsidepix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neugelb%2Fsidepix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neugelb","download_url":"https://codeload.github.com/neugelb/sidepix/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226148890,"owners_count":17581048,"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":["image","next","optimization","picture","react","react-component","sharp"],"created_at":"2024-08-06T08:03:26.946Z","updated_at":"2024-11-24T09:32:30.614Z","avatar_url":"https://github.com/neugelb.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# sidepix\n\n`\u003cpicture\u003e` component with image processing as side-effect.\n\n## Features\n\n- `\u003cpicture\u003e`, not `\u003cimg\u003e` (we're not in [2012](https://thehistoryoftheweb.com/responsive-design-picture-element/) anymore...)\n- Responsively adapt image size and aspect ratio\n- Responsively switch between different images\n- Focal point-aware cropping\n- Arbitrary media queries\n- Multiple file formats (whatever [sharp](https://sharp.pixelplumbing.com/) supports)\n- Fast streaming processing\n- Supports third-party image optimization services\n- UI library- and framework-agnostic\n- Support Server Side Rendering (SSR) and Static Site Generation (SSG)\n- React component included\n- Works well with next/image\n\n## Quick start\n\n```\n$ yarn add sidepix\n```\n\nSee the [Next.js example](/examples/nextjs).\n\n### Create/configure a component\n\nDefine the configuration for your component:\n\n```typescript\nexport const pictureConf: ServerSideConf = {\n  // where clients will get image files from\n  assetsBaseUrl: 'media',\n  // backend processing settings (optional)\n  serverSideProcessor: {                \n    // function that fetches the source images   \n    fetch: typeof window === 'undefined'\n      ? (src) =\u003e fetchUrl(`https://your-cms/your-account/media/${src}`)\n      : () =\u003e {},\n    // where to cache source images\n    originalDir: 'image-cache',\n    // where to save processed images (≈ assetsBaseUrl)\n    processedDir: 'public/media',\n  },\n  // specifies how to convert formats\n  targetFormats: (format) =\u003e {\n    switch (format) {\n      case ImageFormat.JPEG:\n        return [ImageFormat.WEBP, ImageFormat.JPEG];\n      case ImageFormat.PNG:\n        return [ImageFormat.WEBP, ImageFormat.PNG];\n      default:\n        return [format];\n    }\n  },\n};\n```\n\nCreate your component:\n```typescript\n// Points to where pictureConf is defined, so that the processing server can find it.\nconst pictureConfRef = {\n  filePath: resolve(__dirname, '../../../conf/PictureConf'),\n  name: 'pictureConf',\n};\n\nexport const Picture = makePicture(pictureConf, pictureConfRef);\n```\n\n### Use it as a normal component\n\n```jsx\n\u003cPicture\n  sources={{\n    // generates 800w and 1200w (JPEG and WEBP)\n    '(min-width: 840px)': {\n      aspectRatio: 2,\n      widths: [800, 1200],\n      sizes: {\n        '(min-width: 1240px)': '1200px',\n        default: '800px',\n      },\n    },\n    // generates 600w (JPEG and WEBP)\n    '(min-width: 640px)': {\n      widths: [600],\n      aspectRatio: 2 / 3,\n      sizes: {\n        default: '600px',\n      },\n    },\n    // base for all the above, generates 400w (JPEG and WEBP)\n    // also generates the fallback \u003cimg\u003e\n    default: {\n      src: 'example.jpg',\n      aspectRatio: 1,\n      focalPoint: [0.46, 0.14],\n      widths: [400],\n      sizes: {\n        default: '400px',\n      },\n    },\n  }}\n/\u003e\n```\n\nThe following HTML will be rendered:\n```html\n\u003cpicture\u003e\n  \u003csource\n    media=\"(min-width: 840px)\"\n    srcset=\"media/example.jpg_46-14_2_800.webp 800w, media/example.jpg_46-14_2_1200.webp 1200w\"\n    sizes=\"(min-width: 1240px) 1200px,800px\" \n    type=\"image/webp\"\u003e\n  \u003csource\n    media=\"(min-width: 840px)\"\n    srcset=\"media/example.jpg_46-14_2_800.jpeg 800w, media/example.jpg_46-14_2_1200.jpeg 1200w\"\n    sizes=\"(min-width: 1240px) 1200px,800px\"\n    type=\"image/jpeg\"\u003e\n  \u003csource\n    media=\"(min-width: 640px)\"\n    srcset=\"media/example.jpg_46-14_2by3_600.webp 600w\"\n    sizes=\"600px\" type=\"image/webp\"\u003e\n  \u003csource\n    media=\"(min-width: 640px)\"\n    srcset=\"media/example.jpg_46-14_2by3_600.jpeg 600w\"\n    sizes=\"600px\"\n    type=\"image/jpeg\"\u003e\n  \u003csource\n    srcset=\"media/example.jpg_46-14_1_400.webp 400w\"\n    sizes=\"400px\"\n    type=\"image/webp\"\u003e\n  \u003csource\n    srcset=\"media/example.jpg_46-14_1_400.jpeg 400w\"\n    sizes=\"400px\"\n    type=\"image/jpeg\"\u003e\n  \u003cimg src=\"media/example.jpg_400.jpeg\"\u003e\n\u003c/picture\u003e\n```\n\n## How it works\n\nA sidepix picture component looks like any regular component, but when rendering in a Node.js environment it downloads, process, and stores all the images that have been rendered. Note that images are processed *as the component renders on the backend*, not upon request from a client as with image optimization services.\n\nThis mechanism has several advantages:\n\n- you describe what images you want only once, where you use them;\n- only images that are used are generated;\n- the whole thing is pretty isolated and reusable with different UI libraries and frameworks.\n\nBut there are also disadvantages:\n\n- if you forget to render an image it won't be there for users to download - but see below, **Processing images without rendering the component**;\n- care must be taken so as not to leak backend code to the frontend.\n\nAll the processing work is done by a separate process that is spawned on the first rendering of a component. The main reasons for this approach are:\n\n- Prevent image processing from being terminated by the rendering process. Since image processing happens in the background as a side effect, React/Next can't know when it's completed, and will just quit once they're done rendering, leaving some processing jobs unfinished.\n- Prevent conflicts when different workers attempt to work on the same image (as can happen with Next).\n\nThe processing server will keep on running until it's explicitly stopped. This package provides a `sidepix-wait` command that will connect to the processing server, wait for all outstanding jobs to finish, and finally quit the server.\n\n## Processing images without rendering the component\n\nIf you find yourself rendering a sidepix component just for generating images, but then you don't actually use it, then you should consider using `makeGetPictureData`:\n\n```typescript\nexport const getPictureData = makeGetPictureData(pictureConf, pictureConfRef);\n```\n\nThis is also useful to add render time optimization to next/image ([see example](/examples/nextjs)).\n\n## Is this stable?\n\nWe're using a version of this at Neugelb in production, so we can testify to the soundness of the concept. However this is a substantial rewrite and an early release, so expect bugs and API changes.\n\n## Troubleshooting\n\nTo understand what's going on with the processing server you can launch it from a separate terminal:\n\n```\n$ yarn sidepix-start\n```\n\nYour app will find this server and connect to it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneugelb%2Fsidepix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneugelb%2Fsidepix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneugelb%2Fsidepix/lists"}