{"id":21456,"date":"2017-01-11T13:39:47","date_gmt":"2017-01-11T13:39:47","guid":{"rendered":"http:\/\/how_to_develop_a_react_library"},"modified":"2022-03-03T20:48:11","modified_gmt":"2022-03-03T20:48:11","slug":"how_to_develop_a_react_library","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library","title":{"rendered":"How to develop a React library"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Developing a library requires a different approach from developing an application. You must consider the use of the library in someone else\u2019s application and design for it. React is well suited for this purpose. And if the library you are creating is an adapter to another library, you can dynamically generate the component\u2019s properties definition to ensure they are forward compatible. There is however more than one way to achieve the same goal, with some conventions to follow and others to cautiously not follow. In particular, I chose to use the context function even though it is an experimental feature because it is useful when you don\u2019t know, or can\u2019t dictate, the way your library\u2019s components will be utilized.<\/p>\n<h2>The React framework<\/h2>\n<h3>What\u2019s react?<\/h3>\n<p>React is a JavaScript library developed by Facebook that specializes in rendering data as HTML and is widely used by websites such as Netflix, Imgur, Airbnb, and of course Facebook.<\/p>\n<h3>Why develop a React SDK?<\/h3>\n<p>An SDK or library provides a developer access to underlying functionality without exposing the complexity of the implementation. A library is most useful when it exposes the functionality using the syntax, components and methodology of the environment it is deployed in.<\/p>\n<p>In Cloudinary\u2019s case, we already provide a Cloudinary JavaScript library that can be used in any JavaScript based applications. However in order for us to support developers that are using React to create fast and responsive websites, we wanted to provide them with a convenient React library that they could use to deliver media from Cloudinary. In effect, we needed to provide React components that the developer can integrate directly in their application. These components in turn would take advantage of the functionality provided by the Cloudinary Core JavaScript library.\nThe rest of this tutorial demonstrates how I developed our React library. This will not be a comprehensive description of our library but rather a guide that will (hopefully!) help you develop your own ReactJs library.<\/p>\n<h3>Basic requirements<\/h3>\n<p>Cloudinary provides a service for a cloud based storage, manipulation and delivery of images and other media content. Thus at minimum, a Cloudinary React library would need to deliver media resources from Cloudinary, as well as define transformations on those resources. The library should also allow the developer to configure the Cloudinary service and configure the Cloudinary account (represented by the cloud_name) to deliver resources from.<\/p>\n<p>Thus the scope of version 1.0 of our library is:<\/p>\n<ul>\n<li>Configuration<\/li>\n<li>Image tag<\/li>\n<li>Transformation parameters<\/li>\n<\/ul>\n<p>I added code samples to accompany this post in our repository. To review the code, issue the following commands:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>git clone https:\/\/github.com\/cloudinary\/cloudinary-react\ngit checkout library-how-to-step-1\n<\/code><\/pre>\n<p>You will find a similar command line at the beginning of each section below. Use it to review the code described in that section.<\/p>\n<h2>Getting to work<\/h2>\n<h3>Setting up the project<\/h3>\n<p>JavaScript is an implementation of the ECMAScript specifications with support for the different versions of ES differing between browsers and browser versions. As a library developer, you would like your library to be supported in as many clients as possible, and these days, that would imply using ES5.<\/p>\n<p>Yet, while it may be possible to write a React program using plain ES5, React normally utilizes ES6 syntax as well as JSX, packing it all with webpack. ES6 (also known as EcmaScript 6 or ES2015) introduces many new features to JavaScript, including classes and scoped variables. JSX on the other hand, provides a convenient way to include HTML code in JavaScript.\nWhen developing a library you can\u2019t always know how it will be utilized. Thus we need to provide developers with sources that can be accessed directly, as well as a compiled JavaScript distribution file that can be imported directly in the browser.\nIn order to utilize these features, several libraries must be included in your project:<\/p>\n<ul>\n<li>The React libraries.<\/li>\n<li>Babel &#8211; translates ES6 and JSX to the more widely supported ES5.<\/li>\n<li>Webpack &#8211; packing javascript applications, and providing a development server.<\/li>\n<li>The Cloudinary JavaScript library (duh!).<\/li>\n<\/ul>\n<p>Babel and Webpack each require a configuration file. You can see an example of the configuration at <a href=\"https:\/\/github.com\/cloudinary\/cloudinary-react\/tree\/library-how-to-step-1\">https:\/\/github.com\/cloudinary\/cloudinary-react\/tree\/library-how-to-step-1<\/a>.<\/p>\n<h5>Folder structure<\/h5>\n<p>Each component will reside in its own folder. A package.json file is also included in each folder to allow using the <code>\u201cimport \u2018.\/folder_name\u2019;\u201d<\/code> syntax instead of the more verbose <code>\u201cimport \u2018.\/folder_name\/filename\u2019;\u201d<\/code> style.<\/p>\n<p><a href =\"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/expanded_component_folder.png\" target=\"_new\" title=\"Expanded components folder\" alt=\"Expanded components folder\" ><img src = \"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/expanded_component_folder.png\" alt=\"Expanded components folder\"><\/a><\/p>\n<p>An alternative would be to have one JS file per component. When using this approach you also need to include an index.js file that exports the components.<\/p>\n<p><a href =\"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/concise_component_folder.png\" target=\"_new\" title=\"Concise component folder\" alt=\"Concise component folder\"><img src = \"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/concise_component_folder.png\" alt=\"Concise component folder\"><\/a><\/p>\n<p>There are pros and cons to each approach, but I\u2019ll leave that discussion for another day.<\/p>\n<h3>Creating the Image component<\/h3>\n<pre class=\"js-syntax-highlighted\"><code>git checkout library-how-to-step-2\n<\/code><\/pre>\n<p>The first Component we\u2019ll implement will be used to display an image.\nThe image component should:<\/p>\n<ul>\n<li>Accept configuration parameters.<\/li>\n<li>Accept transformation parameters.<\/li>\n<li>Accept an image public ID (the image\u2019s unique identifier).<\/li>\n<li>Render an <code>&lt;img&gt;<\/code> tag with a constructed URL to display the image.<\/li>\n<\/ul>\n<h5>Humble beginnings<\/h5>\n<p>We will start by defining a simple image component. The component will accept a cloud name, public ID and dimensions, and will then create an <code>&lt;img&gt;<\/code> tag to render the image.\nA React component can be defined as a function or as a class. We\u2019ll define all Cloudinary components as classes. The difference between the function and class approach is discussed <a href=\"https:\/\/facebook.github.io\/react\/docs\/state-and-lifecycle.html\">here<\/a>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" 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> React, {Component, PropTypes} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary-core'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Image<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">React<\/span>.<span class=\"hljs-title\">Component<\/span> <\/span>{\n <span class=\"hljs-keyword\">constructor<\/span>(props) {\n   <span class=\"hljs-keyword\">super<\/span>(props);\n }\n\n render() {\n   <span class=\"hljs-keyword\">let<\/span> cl = cloudinary.Cloudinary.new({<span class=\"hljs-attr\">cloud_name<\/span>: <span class=\"hljs-keyword\">this<\/span>.props.cloudName});\n   <span class=\"hljs-keyword\">var<\/span> {publicId, width, height} = <span class=\"hljs-keyword\">this<\/span>.props;\n   <span class=\"hljs-keyword\">var<\/span> url = cl.url(publicId, {width, height, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"scale\"<\/span>});\n   <span class=\"hljs-keyword\">return<\/span> (\n     <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> \/&gt;<\/span><\/span>\n   );\n }\n}\n\nImage.propTypes = {\n <span class=\"hljs-attr\">cloudName<\/span>: PropTypes.string.isRequired,\n <span class=\"hljs-attr\">publicId<\/span>: PropTypes.string.isRequired,\n <span class=\"hljs-attr\">width<\/span>: PropTypes.number,\n <span class=\"hljs-attr\">height<\/span>: PropTypes.number\n};\nImage.defaultProps = {};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>Our component is simple enough.\nIt declares 4 properties as well as their type and whether they are required.\nThe render method, which is invoked whenever the component needs to be rendered, uses the properties values to generate a Cloudinary URL, and returns an <code>&lt;img&gt;<\/code> tag with that URL.<\/p>\n<h5>Seeing is believing<\/h5>\n<p>Unlike an application, we can\u2019t simply run our library to see the results. To test it, we\u2019ll create a simple HTML page named \u201cimage.html\u201d under the \u201csamples\u201d folder.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-meta\">&lt;!DOCTYPE <span class=\"hljs-meta-keyword\">html<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/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\">meta<\/span> <span class=\"hljs-attr\">charset<\/span>=<span class=\"hljs-string\">\"UTF-8\"<\/span>&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Cloudinary Image Component<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\">src<\/span>=<span class=\"hljs-string\">\"..\/node_modules\/react\/dist\/react.js\"<\/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\">src<\/span>=<span class=\"hljs-string\">\"..\/node_modules\/react-dom\/dist\/react-dom.js\"<\/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\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/unpkg.com\/babel-standalone@6.15.0\/babel.min.js\"<\/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\">src<\/span>=<span class=\"hljs-string\">\"..\/node_modules\/cloudinary-core\/cloudinary-core-shrinkwrap.js\"<\/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\">src<\/span>=<span class=\"hljs-string\">\"..\/dist\/cloudinary-react.js\"<\/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\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"root\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/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\">\"text\/babel\"<\/span>&gt;<\/span><span class=\"javascript\">\n <span class=\"hljs-keyword\">let<\/span> Image = cloudinaryReact.Image;\n ReactDOM.render(\n         <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"400\"<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"demo\"<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">\"sample\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span><\/span>,\n   <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'root'<\/span>)\n );\n\n<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/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-2\"><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>It works!<\/p>\n<p>(Notice that we are using the babel-standalone library in order to translate JSX syntax in the browser!)<\/p>\n<h5>Deciding what properties to expose<\/h5>\n<p>A React component is affected by two sets of information: the properties and the state.\nGenerally speaking, properties are public and may be set by a parent of the component. State is private and is not accessed outside the component.<\/p>\n<p>A Cloudinary image delivery URL may accept many parameters that affect the resulting image. Furthermore, Cloudinary keeps coming up with new options almost weekly. For these reasons, we want the component to be <em>forward compatible<\/em> &#8211; that is be able to accept new configuration options in the future.<\/p>\n<p>There are two ways to implement the the configuration and transformation options:<\/p>\n<ul>\n<li>As two properties whose values are a collection of parameters.<\/li>\n<li>As individual options.\nThe former option has the advantage of strongly typing the signature of the component &#8211; we know that there are two properties named, say, options and transformation. In addition, the developer can prepare the options and transformation objects in advance:<\/li>\n<\/ul>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">options<\/span>=<span class=\"hljs-string\">{{cloudName:<\/span>\"<span class=\"hljs-attr\">demo<\/span>\", <span class=\"hljs-attr\">publicId:<\/span>\"<span class=\"hljs-attr\">sample<\/span>\"}} <span class=\"hljs-attr\">transformation<\/span>=<span class=\"hljs-string\">{{width:<\/span> <span class=\"hljs-attr\">100<\/span>, <span class=\"hljs-attr\">crop:<\/span> \"<span class=\"hljs-attr\">crop<\/span>\"}}\/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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>Or,<\/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-keyword\">let<\/span> options={<span class=\"hljs-attr\">publicId<\/span>: <span class=\"hljs-string\">\"dog\"<\/span>, <span class=\"hljs-attr\">secure<\/span>: <span class=\"hljs-literal\">true<\/span>};\n<span class=\"hljs-keyword\">let<\/span> transformation={<span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">\"scale\"<\/span>, <span class=\"hljs-attr\">format<\/span>: <span class=\"hljs-string\">\"jpg\"<\/span>, <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">300<\/span>};\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">options<\/span>=<span class=\"hljs-string\">{options}<\/span> <span class=\"hljs-attr\">transformation<\/span>=<span class=\"hljs-string\">{transformation}<\/span> &gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span><\/span>\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>The disadvantage is that they encapsulate the actual parameters and thus make the component less readable.<\/p>\n<p>Compare this with the latter option. Here each configuration and transformation option is an individual property of the component:<\/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-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"demo\"<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">\"sample\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"100\"<\/span> <span class=\"hljs-attr\">crop<\/span>=<span class=\"hljs-string\">\"scale\"<\/span> <span class=\"hljs-attr\">angle<\/span>=<span class=\"hljs-string\">\"10\"<\/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>I think this is much nicer, don\u2019t you?<\/p>\n<h5>Back to the Future<\/h5>\n<p><code>git checkout library-how-to-step-3<\/code><\/p>\n<p>A React component\u2019s properties must be declared. Property values are checked in development mode and will produce a warning if they are invalid. To keep the definition future-compatible, we need to automatically define all configuration and transformation properties. This is done by listing all configuration and transformation keys from the cloudinary_js library. For simplicity, we define all properties as strings.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">let<\/span> allParams = cloudinary.Configuration.CONFIG_PARAMS.concat(cloudinary.Transformation.PARAM_NAMES);\nImage.VALID_OPTIONS = allParams.map(camelCase);\nImage.propTypes = typesFrom(Image.VALID_OPTIONS);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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>This code segment uses two utility functions, provided by the Util module of the Cloudinary Core JavaScript library:<\/p>\n<ul>\n<li>camelCase &#8211; converts a string to CamelCase<\/li>\n<li>typesFrom creates a properties definition object from an array of property names<\/li>\n<\/ul>\n<h5>Attributes<\/h5>\n<p>When designing the component interface (mainly, the properties of the component), you should carefully consider its utilization. For example, the developer may wish to provide attributes for the resulting tags. To allow the developer to explicitly provide the tag attributes, we will use the Transformation.toHtmlAttributes() method. This method collects all the properties that have a name that starts with \u201chtml_\u201d, adds additional attributes calculated from the input, and returns them all as a set of HTML attributes that can be assigned to an HTML tag.<\/p>\n<p>The spread operator (\u2026) is required to convert the attributes object to key=value sets for the component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">let<\/span> attributes = transformation.toHtmlAttributes();\n<span class=\"hljs-keyword\">return<\/span> (\n <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> {<span class=\"hljs-attr\">...attributes<\/span>} <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> \/&gt;<\/span><\/span>\n);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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<h3>Nested components &#8211; adding the Transformation component<\/h3>\n<p><code>git checkout library-how-to-step-4<\/code><\/p>\n<p>A cloudinary resource can be defined using several transformations which are chained together.\nWe can represent this chain by providing an array of objects to the transformation property. But that brings us back to the complex properties approach which we want to avoid.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"demo\"<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">\"sample\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"100\"<\/span> <span class=\"hljs-attr\">crop<\/span>=<span class=\"hljs-string\">\"scale\"<\/span> <span class=\"hljs-attr\">angle<\/span>=<span class=\"hljs-string\">\"10\"<\/span>\n      <span class=\"hljs-attr\">transformation<\/span>=<span class=\"hljs-string\">{&#91;{width:<\/span> <span class=\"hljs-attr\">100<\/span>, <span class=\"hljs-attr\">crop:<\/span> \"<span class=\"hljs-attr\">crop<\/span>\"}, {<span class=\"hljs-attr\">width:<\/span> <span class=\"hljs-attr\">100<\/span>, <span class=\"hljs-attr\">angle:<\/span> <span class=\"hljs-attr\">-10<\/span>, <span class=\"hljs-attr\">crop:<\/span> \"<span class=\"hljs-attr\">scale<\/span>\"}]}\/&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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>To reduce this complexity, we introduce a new component: Transformation. This is a virtual component, i.e., it does not render to the DOM\/HTML. Instead, it represents a transformation that affects the image component.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"demo\"<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">\"sample\"<\/span>&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"100\"<\/span> <span class=\"hljs-attr\">crop<\/span>=<span class=\"hljs-string\">\"crop\"<\/span>\/&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">angle<\/span>=<span class=\"hljs-string\">\"-10\"<\/span> <span class=\"hljs-attr\">crop<\/span>=<span class=\"hljs-string\">\"scale\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"100\"<\/span>\/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>The Transformation component should not render a DOM node or HTML tag. But its properties should be available to the encapsulating Image component.<\/p>\n<p>Preventing the component from rendering is easy. Simply return null from the render method:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">render() {\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">null<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><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>Access to the encapsulated components is done through the children property (this.props.children). React also provides a convenience method to map over the children.\nYou should check the type of the child before using its properties.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">let<\/span> childrenOptions = React.Children.map(<span class=\"hljs-keyword\">this<\/span>.props.children, child =&gt;{\n <span class=\"hljs-keyword\">if<\/span> (child.type &amp;&amp; child.type.name === <span class=\"hljs-string\">\"Transformation\"<\/span>){\n   <span class=\"hljs-keyword\">return<\/span> getOptions(child.props);\n } <span class=\"hljs-keyword\">else<\/span> {\n   <span class=\"hljs-keyword\">return<\/span> {};\n }\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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<h3>What next?<\/h3>\n<p>So far we created an image component and a transformation component. We figured out a way to declare the properties of the component dynamically to support all the parameters of the underlying cloudinary_js library. We also devised a way for the image component to gather properties from its children.<\/p>\n<p>To complete the CloudinaryReact library we will need to create a <em>video component<\/em>. When you do this you will quickly realize that your are duplicating code between the components. This can be solved by moving shared code to the Util object, or by introducing a common ancestor class to both the image and video component. The React documentation advises the former method (see <a href=\"https:\/\/facebook.github.io\/react\/docs\/composition-vs-inheritance.html#so-what-about-inheritance\">here<\/a> and <a href=\"https:\/\/reactjsnews.com\/composing-components#move-over-mixins-use-higher-order-components\">here<\/a>), but we preferred the latter, as it produces a cleaner and more modular code. What\u2019s your preference?<\/p>\n<p>We will also need to tie our image components to the browser\u2019s resize event in order to provide responsive behavior &#8211; the automatic selection of image size to fit the viewing area.<\/p>\n<p>Another feature that would be useful is the ability to define common configuration parameters for a group of components or the entire page. We chose to use context for this purpose even though the React documentation vehemently advises against it as it is considered to be experimental and may be removed in future releases of React. Instead they suggest using Redux or passing an entire component as a property. This may be right for an application but not for a library. The application the library is used in may not be a redux application. Passing the properties down to the children is also not practical as the components who require the information may not be direct descendants of the parent passing the configuration parameters. On the flip side, using context will require us to provide an alternative solution if the functionality is removed in a future release of React.<\/p>\n<p>All this, however, is beyond the scope of this post. As soon as I get a break from my coding tasks, I\u2019ll post the next post on this subject.<\/p>\n<p><code>git checkout master<\/code><\/p>\n<p>You can review the implementation of these ideas as well as other components in our Cloudinary React library, and of course suggest your solutions in the comments.<\/p>\n<p>Additional Resources:<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/how_to_build_an_image_library_with_react_cloudinary\">How to Build an Image Library with React &amp; Cloudinary<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/getting_a_better_react_ion_with_progressive_web_apps\">Getting a Better React-ion with Progressive Web Apps<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/blog\/reactnyc_building_modern_media_experiences_in_react_apps\">ReactNYC: Building Modern Media Experiences in React Apps<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":21457,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[332,25,177,246],"class_list":["post-21456","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-api","tag-asset-management","tag-javascript","tag-react"],"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>React.js Tutorial: How to develop a React library<\/title>\n<meta name=\"description\" content=\"Learn how to build a library in the popular React framework that anyone can use in their application.\" \/>\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\/how_to_develop_a_react_library\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to develop a React library\" \/>\n<meta property=\"og:description\" content=\"Learn how to build a library in the popular React framework that anyone can use in their application.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2017-01-11T13:39:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-03-03T20:48:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1645220568\/website-2021\/blog\/react_library_post\/react_library_post-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"700\" \/>\n\t<meta property=\"og:image:height\" content=\"467\" \/>\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\/how_to_develop_a_react_library#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to develop a React library\",\"datePublished\":\"2017-01-11T13:39:47+00:00\",\"dateModified\":\"2022-03-03T20:48:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\"},\"wordCount\":6,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA\",\"keywords\":[\"API\",\"Asset Management\",\"Javascript\",\"React\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2017\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\",\"url\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\",\"name\":\"React.js Tutorial: How to develop a React library\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA\",\"datePublished\":\"2017-01-11T13:39:47+00:00\",\"dateModified\":\"2022-03-03T20:48:11+00:00\",\"description\":\"Learn how to build a library in the popular React framework that anyone can use in their application.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA\",\"width\":700,\"height\":467},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to develop a React library\"}]},{\"@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":"React.js Tutorial: How to develop a React library","description":"Learn how to build a library in the popular React framework that anyone can use in their application.","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\/how_to_develop_a_react_library","og_locale":"en_US","og_type":"article","og_title":"How to develop a React library","og_description":"Learn how to build a library in the popular React framework that anyone can use in their application.","og_url":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library","og_site_name":"Cloudinary Blog","article_published_time":"2017-01-11T13:39:47+00:00","article_modified_time":"2022-03-03T20:48:11+00:00","og_image":[{"width":700,"height":467,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1645220568\/website-2021\/blog\/react_library_post\/react_library_post-jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library"},"author":{"name":"","@id":""},"headline":"How to develop a React library","datePublished":"2017-01-11T13:39:47+00:00","dateModified":"2022-03-03T20:48:11+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library"},"wordCount":6,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA","keywords":["API","Asset Management","Javascript","React"],"inLanguage":"en-US","copyrightYear":"2017","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library","url":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library","name":"React.js Tutorial: How to develop a React library","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA","datePublished":"2017-01-11T13:39:47+00:00","dateModified":"2022-03-03T20:48:11+00:00","description":"Learn how to build a library in the popular React framework that anyone can use in their application.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA","width":700,"height":467},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/how_to_develop_a_react_library#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to develop a React library"}]},{"@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\/v1649722618\/Web_Assets\/blog\/react_library_post\/react_library_post.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21456","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=21456"}],"version-history":[{"count":3,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21456\/revisions"}],"predecessor-version":[{"id":23243,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21456\/revisions\/23243"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/21457"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=21456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=21456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=21456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}