{"id":28310,"date":"2022-06-28T09:07:25","date_gmt":"2022-06-28T09:07:25","guid":{"rendered":"http:\/\/build-an-image-library-using-web-components"},"modified":"2025-03-08T14:20:17","modified_gmt":"2025-03-08T22:20:17","slug":"build-an-image-library-using-web-components","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/","title":{"rendered":"Build an Image Library Using Web Components"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Every front-end developer knows about at least one of the popular frameworks like React, Vue, Angular, and Next. They probably know about a few more as well. These frameworks are great for most applications, but there are a few cases you might not want to use them.<\/p>\n<p>You might need to build an app around performance and don\u2019t want the overhead of a framework or you want to build a framework-agnostic component library. The second case is usually the most common. That\u2019s why we\u2019re going to build a simple image library with web components to upload and display images we host in Cloudinary to show off how this can work.<\/p>\n<h2>What are Web Components<\/h2>\n<p>Web components are a set of APIs that let you create custom HTML tags to use in your web apps. It\u2019s basically a way for you to build the same functionality you can in a framework, without one. It works with the shadow DOM so you can build hidden DOM trees and only show them when you want. This lets you build things in the background before they get rendered.<\/p>\n<p>It uses HTML template elements to define the components and you can still use ES modules in your implementation. You don\u2019t <em>need<\/em> a library to work with it because you can build web components with pure HTML and JavaScript, but it does help to have at least a simple library to get things running.<\/p>\n<h2>Initialize the Lit app<\/h2>\n<p>We do need to set up the folder for the project. So create a new folder called <code>image-library<\/code> and run the following command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npm i\n<\/code><\/span><\/pre>\n<p>This will take you through a few questions in the terminal that you can feel free to hit \u201cEnter\u201d all the way through. Now we need to install the <a href=\"https:\/\/lit.dev\/\">Lit<\/a> library to work with web components and build this little app.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npm i lit\n<\/code><\/span><\/pre>\n<p>Just to see how much smaller this library is compared to the frameworks we\u2019re working to replace, take a look in the <code>node_modules<\/code> and see how small it is. This is a huge reason that web components are still used and considered. That and they can be used in any of the frameworks if you need to!<\/p>\n<p>You\u2019ll also need a free Cloudinary account to host all of your pictures for this app. So if you don\u2019t have one, go sign up <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">here<\/a>. You\u2019ll need the <code>cloud name<\/code> and <code>upload preset<\/code> values from your console so we can upload images. Go ahead and make a <code>.env<\/code> file at the root of your project. It\u2019ll look something like this:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">CLOUDINARY_UPLOAD_PRESET=cwp7qiwn\nCLOUDINARY_CLOUD_NAME=fjiweegg\n<\/code><\/span><\/pre>\n<h3>Set up the tsconfig<\/h3>\n<p>We\u2019re going to be using TypeScript throughout this app, so we want to have the configs properly set. Create a new file called <code>tsconfig.json<\/code> at the root of the project and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-wrap-lines\">{\n  <span class=\"hljs-attr\">\"compilerOptions\"<\/span>: {\n    <span class=\"hljs-attr\">\"experimentalDecorators\"<\/span>: <span class=\"hljs-literal\">true<\/span>\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This should be all you need for the app to run, but feel free to update this with your favorite rules. Now that everything is set up, let\u2019s continue with the upload component.<\/p>\n<h2>Make the upload component<\/h2>\n<p>While there is an <a href=\"https:\/\/cloudinary.com\/documentation\/upload_widget\">SDK<\/a> that makes this component for us, we can also write a custom upload widget using a web component to have more control over styles and handling requests. In the <code>image-library<\/code> folder, add a new folder called <code>components<\/code>. In that folder, add a new file called <code>upload-widget.ts<\/code>.<\/p>\n<p>This is where we\u2019ll define the web component. In this file, add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { LitElement, html, css } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lit\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { customElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lit\/decorators.js\"<\/span>;\n\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">uploadReq<\/span>(<span class=\"hljs-params\">e: any<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> uploadApi = <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-subst\">${process.env.CLOUDINARY_CLOUD_NAME}<\/span>\/image\/upload`<\/span>;\n\n  <span class=\"hljs-keyword\">const<\/span> dataUrl = e.target.value;\n\n  <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n\n  formData.append(<span class=\"hljs-string\">\"file\"<\/span>, dataUrl);\n  formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, process.env.CLOUDINARY_UPLOAD_PRESET);\n\n  <span class=\"hljs-keyword\">await<\/span> fetch(uploadApi, {\n    <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n    <span class=\"hljs-attr\">body<\/span>: formData,\n  });\n}\n\n@customElement(<span class=\"hljs-string\">\"upload-widget\"<\/span>)\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">UploadWidget<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n  <span class=\"hljs-keyword\">static<\/span> styles = css`<span class=\"css\">\n    <span class=\"hljs-selector-tag\">button<\/span> {\n      <span class=\"hljs-attribute\">font-size<\/span>: <span class=\"hljs-number\">18px<\/span>;\n      <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">2px<\/span> <span class=\"hljs-number\">5px<\/span>;\n    }\n  `<\/span>;\n\n  render() {\n    <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<\/span><\/span><span class=\"hljs-subst\">${(e: any) =&gt; uploadReq(e)}<\/span><span class=\"xml\"><span class=\"hljs-tag\">&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"imageUploader\"<\/span>&gt;<\/span>Upload an image here<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"imageUploader\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>Add image<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n    `<\/span>;\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Let\u2019s go through what\u2019s happening in the code here. First, we import a few packages. Next, we define a function called <code>uploadReq<\/code>. This will take an uploaded file and send it to our Cloudinary account. You\u2019ll need the credentials for your account to make the POST request work and you can get those from the Cloudinary dashboard.<\/p>\n<p>After the <code>uploadReq<\/code> function, we start defining the component. We use the <code>@customElement<\/code> decorator so that our app will know that this is a custom HTML element called <code>upload-widget<\/code>. Then we define some styles for the widget. Lastly, we build the form that will be rendered when this web component is added to the screen. This form lets users upload any image file they want and it will trigger a POST request to Cloudinary.<\/p>\n<h2>Create the image library<\/h2>\n<p>Now we need to get all of the images from Cloudinary that we want to show in our library. You\u2019ll need a couple more credentials, so go back to your dashboard and get an API key and the corresponding API secret and add those to your <code>.env<\/code> file. So your file should look like this:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\"># .env<\/span>\nCLOUDINARY_UPLOAD_PRESET=cwp7qiwn\nCLOUDINARY_CLOUD_NAME=fnoiqrio\nCLOUDINARY_API_KEY=<span class=\"hljs-number\">49846468468868<\/span>\nCLOUDINARY_API_SECRET=_8qe84fed8gv4tggwr659\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now add a new file to the <code>components<\/code> folder called <code>library-display.ts<\/code>. Add the following code to the file:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ library-display.ts<\/span>\n<span class=\"hljs-keyword\">import<\/span> { LitElement, html, css } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lit\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { customElement } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"lit\/decorators.js\"<\/span>;\n\ninterface Image {\n  <span class=\"hljs-attr\">title<\/span>: string;\n  url: string;\n}\n\n@customElement(<span class=\"hljs-string\">\"library-display\"<\/span>)\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">LibraryDisplay<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">LitElement<\/span> <\/span>{\n  <span class=\"hljs-keyword\">static<\/span> styles = css`<span class=\"css\">\n    <span class=\"hljs-selector-class\">.container<\/span> {\n      <span class=\"hljs-attribute\">display<\/span>: flex;\n      <span class=\"hljs-attribute\">justify-content<\/span>: space-between;\n      <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    }\n    <span class=\"hljs-selector-class\">.image-card<\/span> {\n      <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-number\">8px<\/span>;\n      <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">250px<\/span>;\n    }\n  `<\/span>;\n\n  images: Image&#91;] = &#91;\n    {\n      <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"dogs\"<\/span>,\n      <span class=\"hljs-attr\">url<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/<span class=\"hljs-subst\">${process.env.CLOUDINARY_CLOUD_NAME}<\/span>\/image\/upload\/v1606580778\/3dogs.jpg`<\/span>,\n    },\n    {\n      <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">\"dogs\"<\/span>,\n      <span class=\"hljs-attr\">url<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/<span class=\"hljs-subst\">${process.env.CLOUDINARY_CLOUD_NAME}<\/span>\/image\/upload\/v1606580778\/3dogs.jpg`<\/span>,\n    },\n  ];\n\n  <span class=\"hljs-keyword\">async<\/span> fetchImages() {\n    <span class=\"hljs-keyword\">const<\/span> results = <span class=\"hljs-keyword\">await<\/span> fetch(\n      <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-subst\">${process.env.CLOUDINARY_CLOUD_NAME}<\/span>\/resources\/image`<\/span>,\n      {\n        <span class=\"hljs-attr\">headers<\/span>: {\n          <span class=\"hljs-attr\">Authorization<\/span>: <span class=\"hljs-string\">`Basic <span class=\"hljs-subst\">${Buffer.<span class=\"hljs-keyword\">from<\/span>(\n            process.env.CLOUDINARY_API_KEY +\n              <span class=\"hljs-string\">\":\"<\/span> +\n              process.env.CLOUDINARY_API_SECRET\n          ).toString(<span class=\"hljs-string\">\"base64\"<\/span>)}<\/span>`<\/span>,\n        },\n      }\n    ).then(<span class=\"hljs-function\">(<span class=\"hljs-params\">r<\/span>) =&gt;<\/span> r.json());\n\n    <span class=\"hljs-keyword\">const<\/span> { resources } = results;\n\n    <span class=\"hljs-keyword\">const<\/span> allImgs: Image&#91;] = resources.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">resource<\/span>) =&gt;<\/span> ({\n      <span class=\"hljs-attr\">url<\/span>: resource.secure_url,\n      <span class=\"hljs-attr\">title<\/span>: resource.public_id,\n    }));\n\n    <span class=\"hljs-keyword\">this<\/span>.images = allImgs;\n  }\n\n  render() {\n    <span class=\"hljs-keyword\">this<\/span>.fetchImages();\n    <span class=\"hljs-keyword\">return<\/span> html`<span class=\"xml\">\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n        <\/span><span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.images.length &gt; <span class=\"hljs-number\">0<\/span>\n          ? <span class=\"hljs-keyword\">this<\/span>.images.map(\n              (image) =&gt;\n                html`<span class=\"xml\">\n                  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n                    <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"image-card\"<\/span>\n                    <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${image.url}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span>\n                    <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"<\/span><\/span><\/span><span class=\"hljs-subst\">${image.title}<\/span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-string\">\"<\/span>\n                  \/&gt;<\/span>\n                `<\/span>\n            )\n          : html`<span class=\"xml\"> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>No images<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span> `<\/span>}<\/span><span class=\"xml\">\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    `<\/span>;\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We start this component off with some imports and a type definition. Then we create a new custom <code>library-display<\/code> component. Next we add some styles and then we seed a few images. Make sure you update the image files to something in your own Cloudinary account!<\/p>\n<p>Then we define the <code>fetchImages<\/code> method which will retrieve all of the images we upload to Cloudinary and update the <code>images<\/code> array we have in the component. Finally, we call the <code>render<\/code> function which is where we call <code>fetchImages<\/code>, and then create all of the HTML elements to display the images. That\u2019s all for this component. Now we need to update the <code>index.html<\/code> file to use these custom web components.<\/p>\n<h2>Use the web components<\/h2>\n<p>Open the <code>index.html<\/code> file, delete any existing code and add the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-comment\">&lt;!-- index.html --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>This library though<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\".\/upload-widget.ts\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"module\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\".\/library-display.ts\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">upload-widget<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">upload-widget<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">library-display<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">library-display<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We import the components in the <code>&lt;head&gt;<\/code> and then use them in the <code>&lt;body&gt;<\/code>. That\u2019s all we need to display these to users! So you\u2019ve written a couple of custom web components to handle this whole library for you.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1656086890\/e-603fc55d218a650069f5228b\/hownm0rmetxqixswcfhg.png\" alt=\"finished app\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"573\"\/><\/p>\n<h2>Finished code<\/h2>\n<p>You can check out the complete code for this project in the <a href=\"https:\/\/github.com\/flippedcoder\/media-projects\/tree\/main\/image-library\"><code>image-library<\/code> folder of this repo<\/a>. You can also check it out in <a href=\"https:\/\/codesandbox.io\/s\/headless-lake-xgmtfy\">this Code Sandbox<\/a>.<\/p>\n<p>&lt;CodeSandBox\ntitle=\u201cheadless-lake-xgmtfy\u201d\nid=\u201cheadless-lake-xgmtfy\u201d\n\/&gt;<\/p>\n<h2>Conclusion<\/h2>\n<p>Now that you\u2019ve seen what it\u2019s like to work with web components using Lit, maybe you can convince your team to stray away from those big frameworks. Depending on the type of application, it can be easier to make up your own internal framework as you build.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28311,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,177,371],"class_list":["post-28310","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-javascript","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Build an Image Library Using Web Components<\/title>\n<meta name=\"description\" content=\"Sometimes you might wonder what it&#039;s like to build an app without any frameworks. There aren&#039;t a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That&#039;s why we&#039;re going to explore web components in this post.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build an Image Library Using Web Components\" \/>\n<meta property=\"og:description\" content=\"Sometimes you might wonder what it&#039;s like to build an app without any frameworks. There aren&#039;t a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That&#039;s why we&#039;re going to explore web components in this post.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-06-28T09:07:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-08T22:20:17+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2896\" \/>\n\t<meta property=\"og:image:height\" content=\"1944\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Build an Image Library Using Web Components\",\"datePublished\":\"2022-06-28T09:07:25+00:00\",\"dateModified\":\"2025-03-08T22:20:17+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\"},\"wordCount\":7,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"Javascript\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\",\"name\":\"Build an Image Library Using Web Components\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA\",\"datePublished\":\"2022-06-28T09:07:25+00:00\",\"dateModified\":\"2025-03-08T22:20:17+00:00\",\"description\":\"Sometimes you might wonder what it's like to build an app without any frameworks. There aren't a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That's why we're going to explore web components in this post.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA\",\"width\":2896,\"height\":1944},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build an Image Library Using Web Components\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build an Image Library Using Web Components","description":"Sometimes you might wonder what it's like to build an app without any frameworks. There aren't a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That's why we're going to explore web components in this post.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/","og_locale":"en_US","og_type":"article","og_title":"Build an Image Library Using Web Components","og_description":"Sometimes you might wonder what it's like to build an app without any frameworks. There aren't a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That's why we're going to explore web components in this post.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-06-28T09:07:25+00:00","article_modified_time":"2025-03-08T22:20:17+00:00","og_image":[{"width":2896,"height":1944,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/"},"author":{"name":"","@id":""},"headline":"Build an Image Library Using Web Components","datePublished":"2022-06-28T09:07:25+00:00","dateModified":"2025-03-08T22:20:17+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/"},"wordCount":7,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA","keywords":["Guest Post","Image","Javascript","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/","name":"Build an Image Library Using Web Components","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA","datePublished":"2022-06-28T09:07:25+00:00","dateModified":"2025-03-08T22:20:17+00:00","description":"Sometimes you might wonder what it's like to build an app without any frameworks. There aren't a lot of use cases for this these days, but the few that do come up are usually pretty complex and very performance-focused. That's why we're going to explore web components in this post.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA","width":2896,"height":1944},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/build-an-image-library-using-web-components\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Build an Image Library Using Web Components"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924842\/Web_Assets\/blog\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5\/3b839500e8d37f75b80e4b7e3fab0c076d8d6d49-2896x1944-1_28311ae4c5.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28310","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28310"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28310\/revisions"}],"predecessor-version":[{"id":37153,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28310\/revisions\/37153"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28311"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28310"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28310"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28310"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}