<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Image Placeholder - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Image Placeholder - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 23 Jun 2026 22:45:43 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/image-placeholder/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Building the React Image Optimization Component for Tueri.io ]]>
                </title>
                <description>
                    <![CDATA[ By Dane Stevens Let’s face it, image optimization is hard. We want to make it effortless. When we set out to build our React Component there were a few problems we wanted to solve: Automatically decide image width for any device based on the parent... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-the-react-image-optimization-component-for-tueri-io/</link>
                <guid isPermaLink="false">66d84e855a0b456f4d321452</guid>
                
                    <category>
                        <![CDATA[ Image Placeholder ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image optimization  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ responsive images ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 11 Jun 2019 18:37:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/progressive.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dane Stevens</p>
<p><img src="https://cdn.tueri.io/274877906967/low-quality-image-placeholder-example.png" alt="Low-quality Image Placeholder" width="1428" height="1046" loading="lazy"></p>
<p>Let’s face it, image optimization is hard. We want to make it effortless.</p>
<p>When we set out to build our React Component there were a few problems we wanted to solve:</p>
<ul>
<li><p>Automatically decide image width for any device based on the parent container.</p>
</li>
<li><p>Use the best possible image format the user’s browser supports.</p>
</li>
<li><p>Automatic image lazy loading.</p>
</li>
<li><p>Automatic low-quality image placeholders (LQIP).</p>
</li>
</ul>
<p>Oh, and it had to be effortless for React Developers to use.</p>
<h2 id="heading-this-is-what-we-came-up-with">This is what we came up with:</h2>
<pre><code class="lang-jsx">&lt;Img src={ tueriImageId } alt=<span class="hljs-string">'Alt Text'</span> /&gt;
</code></pre>
<p>Easy right? Let’s dive in.</p>
<h2 id="heading-calculating-the-image-size">Calculating the image size:</h2>
<p>Create a <code>&lt;figure /&gt;</code> element, detect the width and build an image URL:</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Img</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{

    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props)
        <span class="hljs-built_in">this</span>.state = {
            <span class="hljs-attr">width</span>: <span class="hljs-number">0</span>
        }
        <span class="hljs-built_in">this</span>.imgRef = React.createRef()
    }

    componentDidMount() {
        <span class="hljs-keyword">const</span> width = <span class="hljs-built_in">this</span>.imgRef.current.clientWidth
        <span class="hljs-built_in">this</span>.setState({
            width
        })
    }

    render() {

        <span class="hljs-comment">// Destructure props and state</span>
        <span class="hljs-keyword">const</span> { src, alt, options = {}, ext = <span class="hljs-string">'jpg'</span> } = <span class="hljs-built_in">this</span>.props
        <span class="hljs-keyword">const</span> { width } = <span class="hljs-built_in">this</span>.state

        <span class="hljs-comment">// Create an empty query string</span>
        <span class="hljs-keyword">let</span> queryString = <span class="hljs-string">''</span>        

        <span class="hljs-comment">// If width is specified, otherwise use auto-detected width</span>
        options[<span class="hljs-string">'w'</span>] = options[<span class="hljs-string">'w'</span>] || width

        <span class="hljs-comment">// Loop through option object and build queryString</span>
        <span class="hljs-built_in">Object</span>.keys(options).map(<span class="hljs-function">(<span class="hljs-params">option, i</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> queryString +=  <span class="hljs-string">`<span class="hljs-subst">${i &lt; <span class="hljs-number">1</span> ? <span class="hljs-string">'?'</span> : <span class="hljs-string">'&amp;'</span>}</span><span class="hljs-subst">${option}</span>=<span class="hljs-subst">${options[option]}</span>`</span>
        })

        <span class="hljs-keyword">return</span>(
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">figure</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{this.imgRef}</span>&gt;</span>
                { 
                    // If the container width has been set, display the image else null
                    width &gt; 0 ? (
                        <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
                            <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">cdn.tueri.io</span>/${ <span class="hljs-attr">src</span> }/${ <span class="hljs-attr">kebabCase</span>(<span class="hljs-attr">alt</span>) }<span class="hljs-attr">.</span>${ <span class="hljs-attr">ext</span> }${ <span class="hljs-attr">queryString</span> }`}
                            <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span> <span class="hljs-attr">alt</span> }
                        /&gt;</span>
                    ) : null 
                }
            <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span></span>
        )

    }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Img
</code></pre>
<p>This returns the following HTML:</p>
<pre><code class="lang-jsx">&lt;figure&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
        <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tueri.io/tueriImageId/alt-text.jpg?w=autoCalculatedWidth"</span> 
        <span class="hljs-attr">alt</span>=<span class="hljs-string">"Alt Text"</span> 
    /&gt;</span></span>
&lt;/figure&gt;
</code></pre>
<h2 id="heading-use-the-best-possible-image-format">Use the best possible image format:</h2>
<p>Next, we needed to add support for detecting WebP images and having the Tueri service return the image in the WebP format:</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Img</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{

    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-comment">// ...</span>
        <span class="hljs-built_in">this</span>.window = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">'undefined'</span> &amp;&amp; <span class="hljs-built_in">window</span>
        <span class="hljs-built_in">this</span>.isWebpSupported = <span class="hljs-built_in">this</span>.isWebpSupported.bind(<span class="hljs-built_in">this</span>)
    }

    <span class="hljs-comment">// ...</span>

    isWebpSupported() {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.window.createImageBitmap) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    render() {

        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// If a format has not been specified, detect webp support</span>
        <span class="hljs-comment">// Set the fm (format) option in the image URL</span>
        <span class="hljs-keyword">if</span> (!options[<span class="hljs-string">'fm'</span>] &amp;&amp; <span class="hljs-built_in">this</span>.isWebpSupported) {
            options[<span class="hljs-string">'fm'</span>] = <span class="hljs-string">'webp'</span>
        }

        <span class="hljs-comment">// ...</span>

        <span class="hljs-keyword">return</span> (
            <span class="hljs-comment">// ...</span>
        )

    }
}

<span class="hljs-comment">// ...</span>
</code></pre>
<p>This returns the following HTML:</p>
<pre><code class="lang-jsx">&lt;figure&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
        <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.tueri.io/tueriImageId/alt-text.jpg?w=autoCalculatedWidth&amp;fm=webp"</span> 
        <span class="hljs-attr">alt</span>=<span class="hljs-string">"Alt Text"</span> 
    /&gt;</span></span>
&lt;/figure&gt;
</code></pre>
<h2 id="heading-automatic-image-lazy-loading">Automatic image lazy loading:</h2>
<p>Now, we need to find out if the <code>&lt;figure /&gt;</code> element is in the viewport, plus we add a little buffer area so the images load just before being scrolled into view.</p>
<pre><code class="lang-jsx">   <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Img</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{

   <span class="hljs-keyword">constructor</span>(props) {
       <span class="hljs-comment">// ...</span>
       <span class="hljs-built_in">this</span>.state = {
           <span class="hljs-comment">// ...</span>
           <span class="hljs-attr">isInViewport</span>: <span class="hljs-literal">false</span>
           <span class="hljs-attr">lqipLoaded</span>: <span class="hljs-literal">false</span>
       }
       <span class="hljs-comment">// ...</span>
       <span class="hljs-built_in">this</span>.handleViewport = <span class="hljs-built_in">this</span>.handleViewport.bind(<span class="hljs-built_in">this</span>)
   }

   componentDidMount() {
       <span class="hljs-comment">// ...</span>
       <span class="hljs-built_in">this</span>.handleViewport()
       <span class="hljs-built_in">this</span>.window.addEventListener(<span class="hljs-string">'scroll'</span>, <span class="hljs-built_in">this</span>.handleViewport)
   }

   handleViewport() {
       <span class="hljs-comment">// Only run if the image has not already been loaded</span>
       <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.imgRef.current &amp;&amp; !<span class="hljs-built_in">this</span>.state.lqipLoaded) {
           <span class="hljs-comment">// Get the viewport height</span>
           <span class="hljs-keyword">const</span> windowHeight = <span class="hljs-built_in">this</span>.window.innerHeight
           <span class="hljs-comment">// Get the top position of the &lt;figure /&gt; element</span>
           <span class="hljs-keyword">const</span> imageTopPosition = <span class="hljs-built_in">this</span>.imgRef.current.getBoundingClientRect().top
           <span class="hljs-comment">// Multiply the viewport * buffer (default buffer: 1.5)</span>
           <span class="hljs-keyword">const</span> buffer = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">this</span>.props.buffer === <span class="hljs-string">'number'</span> &amp;&amp; <span class="hljs-built_in">this</span>.props.buffer &gt; <span class="hljs-number">1</span> &amp;&amp; <span class="hljs-built_in">this</span>.props.buffer &lt; <span class="hljs-number">10</span> ? <span class="hljs-built_in">this</span>.props.buffer : <span class="hljs-number">1.5</span>
           <span class="hljs-comment">// If &lt;figure /&gt; is in viewport</span>
           <span class="hljs-keyword">if</span> (windowHeight * buffer &gt; imageTopPosition) {
               <span class="hljs-built_in">this</span>.setState({
                   <span class="hljs-attr">isInViewport</span>: <span class="hljs-literal">true</span>
               })
           }
       }
   }

   <span class="hljs-comment">// ...</span>

   componentWillUnmount() {
       <span class="hljs-built_in">this</span>.window.removeEventListener(<span class="hljs-string">'scroll'</span>, <span class="hljs-built_in">this</span>.handleViewport)
   }

   render() {

       <span class="hljs-comment">// Destructure props and state</span>
       <span class="hljs-comment">// ...</span>
       <span class="hljs-keyword">const</span> { isInViewport, width } = <span class="hljs-built_in">this</span>.state

       <span class="hljs-comment">// ...</span>

       <span class="hljs-keyword">return</span> (
           <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">figure</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{this.imgRef}</span>&gt;</span>
               { 
                   // If the container width has been set, display the image else null
                   isInViewport &amp;&amp; width &gt; 0 ? (
                       <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
                           <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{</span> () =&gt;</span> { this.setState({ lqipLoaded: true }) } }
                           // ...
                       /&gt;
                   ) : null 
               }
           <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span></span>
       )

   }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Img
</code></pre>
<h2 id="heading-automatic-low-quality-image-placeholders-lqip">Automatic low-quality image placeholders (LQIP):</h2>
<p>Finally, when an image is in the viewport, we want to load a 1/10 size blurred image, then fade out the placeholder image when the full-size image is loaded:</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Img</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{

    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-comment">// ...</span>
        <span class="hljs-built_in">this</span>.state = {
            <span class="hljs-comment">// ...</span>
            <span class="hljs-attr">fullsizeLoaded</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-comment">// ...</span>

    }

    <span class="hljs-comment">// ...</span>

    render() {

        <span class="hljs-comment">// Destructure props and state</span>
        <span class="hljs-comment">// ...</span>
        <span class="hljs-keyword">const</span> { isInViewport, width, fullsizeLoaded } = <span class="hljs-built_in">this</span>.state

        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Modify the queryString for the LQIP image: replace the width param with a value 1/10 the fullsize</span>
        <span class="hljs-keyword">const</span> lqipQueryString = queryString.replace(<span class="hljs-string">`w=<span class="hljs-subst">${ width }</span>`</span>, <span class="hljs-string">`w=<span class="hljs-subst">${ <span class="hljs-built_in">Math</span>.round(width * <span class="hljs-number">0.1</span>) }</span>`</span>)

        <span class="hljs-comment">// Set the default styles. The full size image should be absolutely positioned within the &lt;figure /&gt; element</span>
        <span class="hljs-keyword">const</span> styles = {
            <span class="hljs-attr">figure</span>: {
                <span class="hljs-attr">position</span>: <span class="hljs-string">'relative'</span>,
                <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>
            },
            <span class="hljs-attr">lqip</span>: {
                <span class="hljs-attr">width</span>: <span class="hljs-string">'100%'</span>,
                <span class="hljs-attr">filter</span>: <span class="hljs-string">'blur(5px)'</span>,
                <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
                <span class="hljs-attr">transition</span>: <span class="hljs-string">'all 0.5s ease-in'</span>
            },
            <span class="hljs-attr">fullsize</span>: {
                <span class="hljs-attr">position</span>: <span class="hljs-string">'absolute'</span>,
                <span class="hljs-attr">top</span>: <span class="hljs-string">'0px'</span>,
                <span class="hljs-attr">left</span>: <span class="hljs-string">'0px'</span>,
                <span class="hljs-attr">transition</span>: <span class="hljs-string">'all 0.5s ease-in'</span>
            }
        }

        <span class="hljs-comment">// When the fullsize image is loaded, fade out the LQIP</span>
        <span class="hljs-keyword">if</span> (fullsizeLoaded) {
            styles.lqip.opacity = <span class="hljs-number">0</span>
        }

        <span class="hljs-keyword">return</span>(
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">figure</span>
                <span class="hljs-attr">style</span>=<span class="hljs-string">{</span> <span class="hljs-attr">styles.figure</span> }
                // <span class="hljs-attr">...</span>
            &gt;</span>
                {
                    isInViewport &amp;&amp; width &gt; 0 ? (
                        <span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span>&gt;</span>

                            {/* Load fullsize image in background */}
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
                                <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{</span> () =&gt;</span> { this.setState({ fullsizeLoaded: true }) } }
                                style={ styles.fullsize }
                                src={`https://cdn.tueri.io/${ src }/${ kebabCase(alt) }.${ ext }${ queryString }`}
                                alt={ alt }
                            /&gt;

                            {/* Load LQIP in foreground */}
                            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
                                <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{</span> () =&gt;</span> { this.setState({ lqipLoaded: true }) } }
                                style={ styles.lqip }
                                src={`https://cdn.tueri.io/${ src }/${ kebabCase(alt) }.${ ext }${ lqipQueryString }`} 
                                alt={ alt } 
                            /&gt;
                        <span class="hljs-tag">&lt;/<span class="hljs-name">React.Fragment</span>&gt;</span>
                    ) : null
                }            
            <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span></span>
        )

    }
}

<span class="hljs-comment">// ...</span>
</code></pre>
<h2 id="heading-putting-it-all-together">Putting it all together:</h2>
<p>Image optimization made effortless. Just swap out your regular <code>&lt;img /&gt;</code> elements for the Tueri <code>&lt;Img /&gt;</code> and never worry about optimization again.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">'prop-types'</span>
<span class="hljs-keyword">import</span> { TueriContext } <span class="hljs-keyword">from</span> <span class="hljs-string">'./Provider'</span>
<span class="hljs-keyword">import</span> kebabCase <span class="hljs-keyword">from</span> <span class="hljs-string">'lodash.kebabcase'</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Img</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{

    <span class="hljs-keyword">constructor</span>(props) {
        <span class="hljs-built_in">super</span>(props)
        <span class="hljs-built_in">this</span>.state = {
            <span class="hljs-attr">isInViewport</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">width</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">height</span>: <span class="hljs-number">0</span>,
            <span class="hljs-attr">lqipLoaded</span>: <span class="hljs-literal">false</span>,
            <span class="hljs-attr">fullsizeLoaded</span>: <span class="hljs-literal">false</span>
        }

        <span class="hljs-built_in">this</span>.imgRef = React.createRef()
        <span class="hljs-built_in">this</span>.window = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">'undefined'</span> &amp;&amp; <span class="hljs-built_in">window</span> 
        <span class="hljs-built_in">this</span>.handleViewport = <span class="hljs-built_in">this</span>.handleViewport.bind(<span class="hljs-built_in">this</span>)       
        <span class="hljs-built_in">this</span>.isWebpSupported = <span class="hljs-built_in">this</span>.isWebpSupported.bind(<span class="hljs-built_in">this</span>)

    }

    componentDidMount() {

        <span class="hljs-keyword">const</span> width = <span class="hljs-built_in">this</span>.imgRef.current.clientWidth

        <span class="hljs-built_in">this</span>.setState({
            width
        })

        <span class="hljs-built_in">this</span>.handleViewport()

        <span class="hljs-built_in">this</span>.window.addEventListener(<span class="hljs-string">'scroll'</span>, <span class="hljs-built_in">this</span>.handleViewport)

    }

    handleViewport() {
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.imgRef.current &amp;&amp; !<span class="hljs-built_in">this</span>.state.lqipLoaded) {
            <span class="hljs-keyword">const</span> windowHeight = <span class="hljs-built_in">this</span>.window.innerHeight
            <span class="hljs-keyword">const</span> imageTopPosition = <span class="hljs-built_in">this</span>.imgRef.current.getBoundingClientRect().top
            <span class="hljs-keyword">const</span> buffer = <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">this</span>.props.buffer === <span class="hljs-string">'number'</span> &amp;&amp; <span class="hljs-built_in">this</span>.props.buffer &gt; <span class="hljs-number">1</span> &amp;&amp; <span class="hljs-built_in">this</span>.props.buffer &lt; <span class="hljs-number">10</span> ? <span class="hljs-built_in">this</span>.props.buffer : <span class="hljs-number">1.5</span>
            <span class="hljs-keyword">if</span> (windowHeight * buffer &gt; imageTopPosition) {
                <span class="hljs-built_in">this</span>.setState({
                    <span class="hljs-attr">isInViewport</span>: <span class="hljs-literal">true</span>
                })
            }

        }
    }

    isWebpSupported() {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.window.createImageBitmap) {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    componentWillUnmount() {
        <span class="hljs-built_in">this</span>.window.removeEventListener(<span class="hljs-string">'scroll'</span>, <span class="hljs-built_in">this</span>.handleViewport)
    }

    render() {

        <span class="hljs-comment">// Destructure props and state</span>
        <span class="hljs-keyword">const</span> { src, alt, options = {}, ext = <span class="hljs-string">'jpg'</span> } = <span class="hljs-built_in">this</span>.props
        <span class="hljs-keyword">const</span> { isInViewport, width, fullsizeLoaded } = <span class="hljs-built_in">this</span>.state

        <span class="hljs-comment">// Create an empty query string</span>
        <span class="hljs-keyword">let</span> queryString = <span class="hljs-string">''</span>

        <span class="hljs-comment">// If width is specified, otherwise use auto-detected width</span>
        options[<span class="hljs-string">'w'</span>] = options[<span class="hljs-string">'w'</span>] || width

        <span class="hljs-comment">// If a format has not been specified, detect webp support</span>
        <span class="hljs-keyword">if</span> (!options[<span class="hljs-string">'fm'</span>] &amp;&amp; <span class="hljs-built_in">this</span>.isWebpSupported) {
            options[<span class="hljs-string">'fm'</span>] = <span class="hljs-string">'webp'</span>
        }

        <span class="hljs-comment">// Loop through option prop and build queryString</span>
        <span class="hljs-built_in">Object</span>.keys(options).map(<span class="hljs-function">(<span class="hljs-params">option, i</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> queryString +=  <span class="hljs-string">`<span class="hljs-subst">${i &lt; <span class="hljs-number">1</span> ? <span class="hljs-string">'?'</span> : <span class="hljs-string">'&amp;'</span>}</span><span class="hljs-subst">${option}</span>=<span class="hljs-subst">${options[option]}</span>`</span>
        })

        <span class="hljs-comment">// Modify the queryString for the LQIP image: replace the width param with a value 1/10 the fullsize</span>
        <span class="hljs-keyword">const</span> lqipQueryString = queryString.replace(<span class="hljs-string">`w=<span class="hljs-subst">${ width }</span>`</span>, <span class="hljs-string">`w=<span class="hljs-subst">${ <span class="hljs-built_in">Math</span>.round(width * <span class="hljs-number">0.1</span>) }</span>`</span>)

        <span class="hljs-keyword">const</span> styles = {
            <span class="hljs-attr">figure</span>: {
                <span class="hljs-attr">position</span>: <span class="hljs-string">'relative'</span>,
                <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>
            },
            <span class="hljs-attr">lqip</span>: {
                <span class="hljs-attr">width</span>: <span class="hljs-string">'100%'</span>,
                <span class="hljs-attr">filter</span>: <span class="hljs-string">'blur(5px)'</span>,
                <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>,
                <span class="hljs-attr">transition</span>: <span class="hljs-string">'all 0.5s ease-in'</span>
            },
            <span class="hljs-attr">fullsize</span>: {
                <span class="hljs-attr">position</span>: <span class="hljs-string">'absolute'</span>,
                <span class="hljs-attr">top</span>: <span class="hljs-string">'0px'</span>,
                <span class="hljs-attr">left</span>: <span class="hljs-string">'0px'</span>,
                <span class="hljs-attr">transition</span>: <span class="hljs-string">'all 0.5s ease-in'</span>
            }
        }

        <span class="hljs-comment">// When the fullsize image is loaded, fade out the LQIP</span>
        <span class="hljs-keyword">if</span> (fullsizeLoaded) {
            styles.lqip.opacity = <span class="hljs-number">0</span>
        }

        <span class="hljs-keyword">const</span> missingALt = <span class="hljs-string">'ALT TEXT IS REQUIRED'</span>

        <span class="hljs-keyword">return</span>(
            <span class="hljs-comment">// Return the CDN domain from the TueriProvider</span>
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TueriContext.Consumer</span>&gt;</span>
                {({ domain }) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">figure</span>
                        <span class="hljs-attr">style</span>=<span class="hljs-string">{</span> <span class="hljs-attr">styles.figure</span> }
                        <span class="hljs-attr">ref</span>=<span class="hljs-string">{this.imgRef}</span>
                    &gt;</span>
                        {
                            // 
                            isInViewport &amp;&amp; width &gt; 0 ? (
                                <span class="hljs-tag">&lt;<span class="hljs-name">React.Fragment</span>&gt;</span>

                                    {/* Load fullsize image in background */}
                                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
                                        <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{</span> () =&gt;</span> { this.setState({ fullsizeLoaded: true }) } }
                                        style={ styles.fullsize }
                                        src={`${ domain }/${ src }/${ kebabCase(alt || missingALt) }.${ ext }${ queryString }`}
                                        alt={ alt || missingALt }
                                    /&gt;

                                    {/* Load LQIP in foreground */}
                                    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
                                        <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{</span> () =&gt;</span> { this.setState({ lqipLoaded: true }) } }
                                        style={ styles.lqip }
                                        src={`${ domain }/${ src }/${ kebabCase(alt || missingALt) }.${ ext }${ lqipQueryString }`} 
                                        alt={ alt || missingALt } 
                                    /&gt;
                                <span class="hljs-tag">&lt;/<span class="hljs-name">React.Fragment</span>&gt;</span>
                            ) : null
                        }            
                    <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>
                )}

            <span class="hljs-tag">&lt;/<span class="hljs-name">TueriContext.Consumer</span>&gt;</span></span>
        )

    }
}

Img.propTypes = {
    <span class="hljs-attr">src</span>: PropTypes.string.isRequired,
    <span class="hljs-attr">alt</span>: PropTypes.string.isRequired,
    <span class="hljs-attr">options</span>: PropTypes.object,
    <span class="hljs-attr">ext</span>: PropTypes.string,
    <span class="hljs-attr">buffer</span>: PropTypes.number
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Img
</code></pre>
<h2 id="heading-see-it-in-action">See it in action:</h2>
<p>Try out a live demo on CodeSandbox:</p>
<div class="embed-wrapper">
        <iframe width="100%" height="350" src="https://codesandbox.io/embed/qjzqw7w3q?fontsize=14" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="CodeSandbox embed" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin" loading="lazy"></iframe></div>
<hr>
<p>_Originally published at <a target="_blank" href="https://tueri.io/blog/2019-03-21-building-the-react-image-optimization-component/?utm_source=Freecodecamp&amp;utm_medium=Post&amp;utm_campaign=">Tueri.io</a>_</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Your complete guide to truly responsive images ]]>
                </title>
                <description>
                    <![CDATA[ By Dane Stevens There is a lot that goes into making a website responsive, and images are a major factor and can make or break your site. With that out of the way, let's dig in: <img src="giraffe.jpg" /> Whoa! That's not quite right, let's fix tha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/your-complete-guide-to-truly-responsive-images/</link>
                <guid isPermaLink="false">66d84e8d39c4dccc43d4d47e</guid>
                
                    <category>
                        <![CDATA[ Progressive Images ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image optimization  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Image Placeholder ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Responsive Image ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 11 Jun 2019 18:30:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/giraffe-NOT-responsive-image-medium.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dane Stevens</p>
<p><img src="https://cdn.tueri.io/274877906986/giraffe-family.jpg" alt="Responsive Images" width="4294" height="2859" loading="lazy"></p>
<p>There is a lot that goes into making a website responsive, and images are a major factor and can make or break your site. With that out of the way, let's dig in:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"giraffe.jpg"</span> /&gt;</span>
</code></pre>
<p><img src="https://cdn.tueri.io/274877906980/giraffe-NOT-responsive-image.jpg?w=700&amp;crop.width=700&amp;h=467&amp;crop.height=467&amp;crop.x=1100&amp;crop.y=700" alt="Non-responsive Girafee" width="700" height="467" loading="lazy"></p>
<p>Whoa! That's not quite right, let's fix that.</p>
<h2 id="heading-percentage-width-images-using-css">Percentage width images using CSS</h2>
<p>By definition, a responsive site has no fixed width. We can account for this by setting our image width using percentages. A percentage width can be set on the image directly using inline CSS or set globally using a CSS style sheet.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Inline CSS --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"giraffe.jpg"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"max-width: 100%;"</span> /&gt;</span>
</code></pre>
<pre><code class="lang-css"><span class="hljs-comment">/* Global CSS style sheet */</span>
<span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
}
</code></pre>
<p>Our image set to 100% maximum width now looks like this. Feel free to resize your browser window and see the image width change automatically.</p>
<p><img src="https://cdn.tueri.io/274877906980/giraffe-NOT-responsive-image.jpg?w=700&amp;h=467" alt="Fully-responsive Giraffe" width="700" height="467" loading="lazy"></p>
<p>Awesome! Our images are now responsive, but let's not get too excited. CSS percentage widths have a major downside: This reading pane has a maximum width of 700 pixels and our image is 3742 pixels wide or 532% larger than what we need. It's also consuming 1.55 MB of bandwidth.</p>
<h3 id="heading-so-the-image-is-huge-whats-the-big-deal">So the image is huge, what's the big deal?</h3>
<p>On a standard ADSL internet connection it is going to take 2 seconds to download that single image.</p>
<p>I'm going to assume you have more than one image on your website. If every image takes 2 seconds to download, your site will be very slow and your search ranking will take a big hit.</p>
<h3 id="heading-how-can-we-fix-this-problem">How can we fix this problem?</h3>
<p>Since we only need an image that is 700 pixels wide, let's pop open Photoshop and resize it.</p>
<p><img src="https://cdn.tueri.io/274877906980/giraffe-NOT-responsive-image.jpg?w=700&amp;h=467" alt="Giraffe Eating" width="700" height="467" loading="lazy"></p>
<p>That's better! Our image is now 700 pixels wide and weighs in at 264 KB with no loss of quality.</p>
<h3 id="heading-what-about-mobile-devices">What about mobile devices?</h3>
<p>Great question! On mobile devices, 700 pixels can be more than double what you need and 264 KB on is still slow on a mobile internet connection.</p>
<p>What if we could display different size images for different size devices?</p>
<p>Now you're thinking!</p>
<h2 id="heading-different-image-sizes-using-the-srcset-attribute">Different image sizes using the srcset attribute</h2>
<p>Let's downsize our original image and save it as three versions of 700 pixels, 480 pixels and 360 pixels. We'll name them as follows:</p>
<ul>
<li>giraffe-small.jpg - 360px @ 101 KB</li>
<li>giraffe-medium.jpg - 480px @ 151 KB</li>
<li>giraffe-large.jpg - 700px @ 264 KB</li>
</ul>
<p><img src="https://cdn.tueri.io/274877906987/giraffe-family.jpg" alt="Small Medium and Large Giraffe" width="933" height="467" loading="lazy"></p>
<p>We'll use the srcset attribute to tell the browser about our different image sizes. This tells the browser that we have three versions of this image in the sizes 360w, 480w and 700w. The "w" in this case is the same as "px".</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
    <span class="hljs-attr">style</span>=<span class="hljs-string">"max-width: 100%;"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"giraffe-small.jpg 360w, giraffe-medium.jpg 480w, giraffe-large.jpg 700w"</span>
/&gt;</span>
</code></pre>
<p>Some older browsers will ignore the srcset attribute. We can use the src attribute as a fallback for these browsers.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
    <span class="hljs-attr">style</span>=<span class="hljs-string">"max-width: 100%;"</span>
    <span class="hljs-attr">srcset</span>=<span class="hljs-string">"giraffe-small.jpg 360w, giraffe-medium.jpg 480w, giraffe-large.jpg 700w"</span>
    <span class="hljs-attr">src</span>=<span class="hljs-string">"giraffe-large.jpg"</span>
/&gt;</span>
</code></pre>
<p><img src="https://cdn.tueri.io/274877906980/giraffe-NOT-responsive-image.jpg?w=700&amp;h=467" alt="Giraffe Eating" width="700" height="467" loading="lazy"></p>
<p>Great! Now we are saving bandwidth by delivering different images to different devices.</p>
<p>This goes without saying, but you know what this means right? You'll need to create a minimum of three versions of every image on your site. If you want to support hi-dpi or retina displays, you'll need even more variations. That is an incredible amount of time that none of us have. In addition, if you re-design your site at different breakpoints, you'll need to do this all over again.</p>
<p>At Tueri, we're developers too and recognize that your time is valuable. In the next section I'll show you how we solved this problem for you.</p>
<h2 id="heading-real-time-image-processing-with-tueriiohttpstueriio">Real-time image processing with <a target="_blank" href="https://tueri.io">Tueri.io</a></h2>
<p><a target="_blank" href="https://tueri.io">Tueri.io</a> is a real-time image processing platform. We store, process, and deliver your image perfectly sized to every device.</p>
<h3 id="heading-heres-what-you-do">Here's what you do</h3>
<ol>
<li>Upload your image to <a target="_blank" href="https://tueri.io">Tueri.io</a></li>
<li>Change the image <code>src</code> to <code>tueri-src</code></li>
<li>Include <a target="_blank" href="https://github.com/tueriapp/vanilla-tueri">tueri.js</a> in your code</li>
</ol>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">tueri-src</span>=<span class="hljs-string">"https://cdn.tueri.io/274877906982/giraffe-family.jpg"</span>/&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"tueri.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p><img src="https://cdn.tueri.io/274877906982/giraffe-family.jpg" alt="Giraffe Family" width="4562" height="3650" loading="lazy"></p>
<h3 id="heading-its-like-magic">It's like magic!</h3>
<p>Oh, and we also do:</p>
<ul>
<li>Low-quality image placeholders</li>
<li>Image lazy-loading</li>
<li>Image compression</li>
<li>Image conversion</li>
</ul>
<hr>
<p>_Originally published at <a target="_blank" href="https://tueri.io/blog/2019-03-27-your-complete-guide-to-truly-responsive-images/?utm_source=Freecodecamp&amp;utm_medium=Post&amp;utm_campaign=">Tueri.io</a>_</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
