<![CDATA[Vojtech Ruzicka's Programming Blog]]>https://www.vojtechruzicka.comRSS for NodeThu, 01 Oct 2020 08:53:52 GMT<![CDATA[Netlify build plugins: 3x faster GatsbyJS build]]>https://www.vojtechruzicka.com/gatsby-build-netlify-plugins/https://www.vojtechruzicka.com/gatsby-build-netlify-plugins/Wed, 13 May 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAC/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAdqGahIof//EABsQAAIDAAMAAAAAAAAAAAAAAAACAQMSBBEi/9oACAEBAAEFAlwbRZbk19w0MWeDUn//xAAWEQEBAQAAAAAAAAAAAAAAAAAAERL/2gAIAQMBAT8BzEf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwGq/8QAHhAAAQMEAwAAAAAAAAAAAAAAAAECIREyQZFRcYH/2gAIAQEABj8C57JZ6Q0t0ItVMaP/xAAcEAEAAwACAwAAAAAAAAAAAAABABEhMVFBcYH/2gAIAQEAAT8hXKw9BZfBgF4qGq6e4FoF8qoEgnWy/a+4/9oADAMBAAIAAwAAABAHH//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/EAx//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8QWx//xAAdEAEAAgEFAQAAAAAAAAAAAAABABEhMUFRYaFx/9oACAEBAAE/EBpQDYDxRKt+ZgRa3LioRNQAw2CmbV+1DWlUNQcW2eQCDL2viT//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/daf200eb791f4815c5f4afaf38ca642e/c54d4/gatsby-build-netlify-plugins.webp 175w, /static/daf200eb791f4815c5f4afaf38ca642e/a3432/gatsby-build-netlify-plugins.webp 350w, /static/daf200eb791f4815c5f4afaf38ca642e/426ac/gatsby-build-netlify-plugins.webp 700w, /static/daf200eb791f4815c5f4afaf38ca642e/c139f/gatsby-build-netlify-plugins.webp 1050w, /static/daf200eb791f4815c5f4afaf38ca642e/7f403/gatsby-build-netlify-plugins.webp 1400w, /static/daf200eb791f4815c5f4afaf38ca642e/fad48/gatsby-build-netlify-plugins.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/daf200eb791f4815c5f4afaf38ca642e/e52aa/gatsby-build-netlify-plugins.jpg 175w, /static/daf200eb791f4815c5f4afaf38ca642e/70ebb/gatsby-build-netlify-plugins.jpg 350w, /static/daf200eb791f4815c5f4afaf38ca642e/29d31/gatsby-build-netlify-plugins.jpg 700w, /static/daf200eb791f4815c5f4afaf38ca642e/9ecec/gatsby-build-netlify-plugins.jpg 1050w, /static/daf200eb791f4815c5f4afaf38ca642e/d165a/gatsby-build-netlify-plugins.jpg 1400w, /static/daf200eb791f4815c5f4afaf38ca642e/b17f8/gatsby-build-netlify-plugins.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/daf200eb791f4815c5f4afaf38ca642e/29d31/gatsby-build-netlify-plugins.jpg" alt="3x faster GatsbyJS build with netlify plugins" title="3x faster GatsbyJS build with netlify plugins" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="netlify-build-plugins" style="position:relative;"><a href="#netlify-build-plugins" aria-label="netlify build plugins permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Netlify build plugins</h2> <p>Netlify build plugins allow you to run custom plugins as a part of your Nelify build process. This allows you to alter your build and run some additional actions.</p> <h2 id="enabling-plugins" style="position:relative;"><a href="#enabling-plugins" aria-label="enabling plugins permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enabling plugins</h2> <p>Netlify build plugins are not enabled by default. This feature is still in beta (as of 5/2020). You can, however, explicitly enable beta build plugin support. Just click on the link <code class="language-text">Build Plugins</code> in the main navigation.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 14.857142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAj0lEQVQI112Nyw6CMBBF+wMiKEJjUmjL9EFbHkZdiIms3fH/X+NdGBcmJyeTm7kzjK9vdV2GZZXjLTxe7v4EB+13ossa+gNhoT2nUMY53zZW+am26dxPtUvcj6B2w7HrC+Vw4ud9a0DWGqFcR0Gb6NKFoYbVXNkSlrakAJ8oIITRrGzC/P3ckJCWTCSboh8/DXcYVqE4+qsAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/4403abbf17615efa83a68a35e04a1144/c54d4/netlify-enabling-build-plugins.webp 175w, /static/4403abbf17615efa83a68a35e04a1144/a3432/netlify-enabling-build-plugins.webp 350w, /static/4403abbf17615efa83a68a35e04a1144/426ac/netlify-enabling-build-plugins.webp 700w, /static/4403abbf17615efa83a68a35e04a1144/c139f/netlify-enabling-build-plugins.webp 1050w, /static/4403abbf17615efa83a68a35e04a1144/7f403/netlify-enabling-build-plugins.webp 1400w, /static/4403abbf17615efa83a68a35e04a1144/5540a/netlify-enabling-build-plugins.webp 1844w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/4403abbf17615efa83a68a35e04a1144/4edbd/netlify-enabling-build-plugins.png 175w, /static/4403abbf17615efa83a68a35e04a1144/13ae7/netlify-enabling-build-plugins.png 350w, /static/4403abbf17615efa83a68a35e04a1144/8c557/netlify-enabling-build-plugins.png 700w, /static/4403abbf17615efa83a68a35e04a1144/e996b/netlify-enabling-build-plugins.png 1050w, /static/4403abbf17615efa83a68a35e04a1144/2cefc/netlify-enabling-build-plugins.png 1400w, /static/4403abbf17615efa83a68a35e04a1144/80cfc/netlify-enabling-build-plugins.png 1844w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/4403abbf17615efa83a68a35e04a1144/8c557/netlify-enabling-build-plugins.png" alt="Netlify build plugins link" title="Netlify build plugins link" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Now simply click <code class="language-text">Enable the beta</code> button:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 32%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAABIElEQVQY01VPy0rDQBSd//AX3Pszrl37F+6qCNKCG6Hiti7UhWBXRahFAxqSNDW0ag0p6eR2pmke87hx0lKoh7u4r3POvcQZ+iZs13N9n0UvUuRKY7WDUqlMikzJTAhTeo2P0YVjEtRIKEACQBOY04SnxYIxxrmUUimFWKvcTr9PA/d87F1+jZYog5Y764ZYkzWZJxBTwwXOOFsDAOZGCRZKK7MzSNlB927/sXMVTmpDrLDS2lARiRJCC8FXK5bny7JIy9Kcp+sRbpz7HEi7ude56cSh8xsdNq8nMV2rIGkP7YbVt+KZ1JoZsiwLJXd/tlfLY+f1zHGsCAJKW0894zqA+CGakl74cz/+nKZ8I1b9h6nltlmae7b9o7fnE+/9D8Y8R8WeIAROAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/8253631578687e0dda1cd56c48920fe7/c54d4/netlify-enabling-build-plugins-2.webp 175w, /static/8253631578687e0dda1cd56c48920fe7/a3432/netlify-enabling-build-plugins-2.webp 350w, /static/8253631578687e0dda1cd56c48920fe7/426ac/netlify-enabling-build-plugins-2.webp 700w, /static/8253631578687e0dda1cd56c48920fe7/c139f/netlify-enabling-build-plugins-2.webp 1050w, /static/8253631578687e0dda1cd56c48920fe7/7f403/netlify-enabling-build-plugins-2.webp 1400w, /static/8253631578687e0dda1cd56c48920fe7/92e91/netlify-enabling-build-plugins-2.webp 2293w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/8253631578687e0dda1cd56c48920fe7/4edbd/netlify-enabling-build-plugins-2.png 175w, /static/8253631578687e0dda1cd56c48920fe7/13ae7/netlify-enabling-build-plugins-2.png 350w, /static/8253631578687e0dda1cd56c48920fe7/8c557/netlify-enabling-build-plugins-2.png 700w, /static/8253631578687e0dda1cd56c48920fe7/e996b/netlify-enabling-build-plugins-2.png 1050w, /static/8253631578687e0dda1cd56c48920fe7/2cefc/netlify-enabling-build-plugins-2.png 1400w, /static/8253631578687e0dda1cd56c48920fe7/452b3/netlify-enabling-build-plugins-2.png 2293w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/8253631578687e0dda1cd56c48920fe7/8c557/netlify-enabling-build-plugins-2.png" alt="Netlify build plugins enable button" title="Netlify build plugins enable button" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Now you need to select which repositories should be enabled for plugins. Note that for sites to be available for plugins, they need to be based on the <code class="language-text">Xenial</code> image. New sites should be based on this by default, but for older sites, you might need to change the image explicitly. You can find this configuration under your <a href="https://docs.netlify.com/configure-builds/get-started/#build-image-selection">site settings</a>:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">Settings → Build &amp; Deploy → Build image selection</code></pre></div> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 57.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABIElEQVQoz41S7W7DIBDr++4Jp/3aO0zaVGla1QbWJm1I+EqAI5kv6apm6qqeHGRAxofDqhCSIaWQUn7vifL4cK0SUR+4YowppWEYrrcxzUDOwzTOZC4WN02r8GndtCAtGKa1UrVq6oahjXXeW+tAzDRax4XDVt77ruswn5Y8gF0sQtD1PZqa/S9uC2djDETXrRIRzJVi/+PpBP7vnUMMoQ+Le+astT53rtTUSIc4bojPggmZh/Gtrj6KHaLfCrETYlsUn1+bsjrOgd0QL5zHEeH7uTgCzgLBsD/RQmxicDEClhFcip6ROiJPKWS6958hOHi3d1ZYLUxb6FYy0aWzIJV398Qvu02KET39eR4PvbCn1+dDWb6v17jP8JvcBffrB7V6vAH4IgPHAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/f48498ede0bf75791adab70f6182563c/c54d4/netlify-build-image-selection.webp 175w, /static/f48498ede0bf75791adab70f6182563c/a3432/netlify-build-image-selection.webp 350w, /static/f48498ede0bf75791adab70f6182563c/426ac/netlify-build-image-selection.webp 700w, /static/f48498ede0bf75791adab70f6182563c/c139f/netlify-build-image-selection.webp 1050w, /static/f48498ede0bf75791adab70f6182563c/7f403/netlify-build-image-selection.webp 1400w, /static/f48498ede0bf75791adab70f6182563c/1a117/netlify-build-image-selection.webp 1414w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/f48498ede0bf75791adab70f6182563c/4edbd/netlify-build-image-selection.png 175w, /static/f48498ede0bf75791adab70f6182563c/13ae7/netlify-build-image-selection.png 350w, /static/f48498ede0bf75791adab70f6182563c/8c557/netlify-build-image-selection.png 700w, /static/f48498ede0bf75791adab70f6182563c/e996b/netlify-build-image-selection.png 1050w, /static/f48498ede0bf75791adab70f6182563c/2cefc/netlify-build-image-selection.png 1400w, /static/f48498ede0bf75791adab70f6182563c/0c3d0/netlify-build-image-selection.png 1414w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/f48498ede0bf75791adab70f6182563c/8c557/netlify-build-image-selection.png" alt="Netlify build image configuration" title="Netlify build image configuration" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>After selecting repositories, you can see the list of available plugins.</p> <h2 id="configuration" style="position:relative;"><a href="#configuration" aria-label="configuration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Configuration</h2> <p>Plugins are not enabled and configured directly from the Netlify UI. Instead, you enable and configure your build plugins in <code class="language-text">netlify.toml</code> file, which should be located inside of your project on the root level.</p> <p>The configuration for <a href="https://github.com/jlengstorf/netlify-plugin-gatsby-cache">netlify-plugin-gatsby-cache</a> can be as simple as this:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">[build] publish = &quot;public&quot; [[plugins]] package = &quot;netlify-plugin-gatsby-cache&quot;</code></pre></div> <p>If you have plugins enabled for your site and this configuration file is present, Netlify will automatically trigger any build plugins defined in your file with the provided settings.</p> <h2 id="gatsby-cache-plugin" style="position:relative;"><a href="#gatsby-cache-plugin" aria-label="gatsby cache plugin permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Gatsby cache plugin</h2> <p>This blog uses GatsbyJS as a static site generator. Originally, the build would take quite a lot of time. This is usually not a problem with GatsbyJS - only the original build takes long, but the subsequent builds are much faster due to Gatsby's cache. Netlify does not preserve this cache by default, so each build is long.</p> <p>Even if the deployment time is not critical for your application, it can help a lot to improve it. That's because after running out of build minutes, you have to pay for more. And the free plan only currently offers 300 build minutes.</p> <p>Fortunately, there is a Netlify build plugin called <a href="https://github.com/jlengstorf/netlify-plugin-gatsby-cache">netlify-plugin-gatsby-cache</a>, which fixes this and makes the subsequent builds much faster.</p> <p>My current site is as follows:</p> <ul> <li>4087 image thumbnails (various sizes)</li> <li>119 GraphQL queries</li> <li>92 articles</li> </ul> <p>Without the plugin the <a href="https://app.netlify.com/sites/vojtechruzicka/deploys/5ea34fc56c47bb0006f3e03a">build takes</a> <strong>13m 20s</strong>. With the plugin <a href="https://app.netlify.com/sites/vojtechruzicka/deploys/5ea35478f6337a0007874256">it takes</a> <strong>3m 44s</strong>. That is 3.57x faster!</p> <p>The time can vary as it depends on the size of your site and the changes that you make.</p> <p>The official docs also <a href="https://github.com/jlengstorf/netlify-plugin-gatsby-cache#how-much-of-a-difference-does-this-plugin-make-in-build-times">provide examples of time reduction</a>:</p> <table> <thead> <tr> <th>Site Size</th> <th>No Cache</th> <th>Cache</th> <th>Savings</th> </tr> </thead> <tbody> <tr> <td><ul><li>231 GraphQL queries</li><li>1,871 images</li><li>224 pages</li></ul></td> <td>293207ms (<a href="https://app.netlify.com/sites/lengstorf/deploys/5dceed27d58a580008daaccc" rel="nofollow">build log</a>)</td> <td>72835ms (<a href="https://app.netlify.com/sites/lengstorf/deploys/5dcef2463da4810008d48aaa" rel="nofollow">build log</a>)</td> <td>75%</td> </tr> <tr> <td><ul><li>5 GraphQL queries</li><li> No image processing</li><li> 32 pages</li></ul></td> <td>22072ms (<a href="https://app.netlify.com/sites/build-plugin-test/deploys/5dceed49e746a200091c76fe" rel="nofollow">build log</a>)</td> <td>15543ms (<a href="https://app.netlify.com/sites/build-plugin-test/deploys/5dceedbfad95d0000bcd46d1" rel="nofollow">build log</a>)</td> <td>30%</td> </tr> </tbody> </table> <h2 id="more-plugins" style="position:relative;"><a href="#more-plugins" aria-label="more plugins permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>More plugins</h2> <p>There are currently 20 plugins available, featured on the Netlify site. Here are some of the interesting ones:</p> <ul> <li><a href="https://github.com/sw-yx/netlify-plugin-a11y">netlify-plugin-a11y</a>: Validate your site with pa11y and fail the build if there are accessibility problems.</li> <li><a href="https://github.com/tkadlec/netlify-build-plugin-speedcurve">netlify-build-plugin-speedcurve</a>: Test your performance using SpeedCurve after each deploy.</li> <li><a href="https://github.com/munter/netlify-plugin-checklinks">netlify-plugin-checklinks</a>: Check your site for broken links.</li> <li><a href="https://github.com/Tom-Bonnike/netlify-plugin-inline-source">netlify-plugin-inline-source</a>: Inline some of your assets to reduce the amount of HTTP request and increase performance.</li> <li><a href="https://github.com/netlify-labs/netlify-plugin-sitemap">netlify-plugin-sitemap</a>: Automatically generate sitemap of your site.</li> <li><a href="https://github.com/sw-yx/netlify-plugin-rss">netlify-plugin-rss</a>: Generate RSS feed for your site.</li> </ul> <h2 id="additional-resources" style="position:relative;"><a href="#additional-resources" aria-label="additional resources permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Additional resources</h2> <ul> <li><a href="https://www.netlify.com/build/plugins-beta/">Introducing Netlify Build Plugins</a></li> <li><a href="https://www.netlify.com/blog/2020/04/30/whats-a-netlify-build-plugin-series-part-1-using-build-plugins/">What's a Netlify Build Plugin Series</a></li> <li><a href="https://docs.netlify.com/configure-builds/build-plugins/">Build plugins documentation</a></li> <li><a href="https://www.netlify.com/blog/2019/10/16/creating-and-using-your-first-netlify-build-plugin/">How to make your own build plugin</a></li> </ul>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[CSS Flexbox tutorial]]>https://www.vojtechruzicka.com/css-flexbox/https://www.vojtechruzicka.com/css-flexbox/Fri, 24 Apr 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/4a87770169419a3eb059f7414d1fe150/3dde1/css-flexbox.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.57142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAC4jAAAuIwF4pT92AAACuklEQVQoz3WK+VMSARTH929rRkDzAPuBS0wzDfFqNCkPvCAEL0ABJV08WkFxBTlW0AAdQcSRFRQvcERFVMaDRsyOodjWmn7s877vzXvf9wWS6+HUwRV2k0onMezuR+IsEDtbxRLnn3ejp+uhh7P4/U3y5zcseo25g5e7W8v30QCGxdKpcCrhAULubdOgNWBcv0FjUX/Q4zcEj5DIkVczp+sZBmes1pXtg+DJV/T4wREIenYcl3F3On2IYREfagA+GtZpxWM0hrymVtcmnWxRdQlBuQTSitWQSu9ET87jD9/D53fevfjcyp7TF/Dtooen6GEMbZMpAZ7cxh9afVGnf9lgVMBOIQjxBrTdaocU8iMbicjtr52TBOIKQfP7/ZMeFbwGWfyjRnvniKGkAQZU8KY3/GXS5BfINLDdZnB8Mi8uIhaXdfHKsZqcs20pRxHlqA3UWPsG1RIlpNUuQFpDr9rRCR4BgwN2UINOzzhtC0gAXbq92L8+3jx1ryR3LhI71xtmp2Vah5hnbVYYsZg2l12Xfr/PtTZh8I7BEWBc8M6ulsx+kOkUPONwBwIKzCMdJkWbRdl+7Fq62wjA0sbxvtoJab1G3qFXCU0qvna4zzwzPiXjA3IuJaRtnuquay3NEFbkCDjZ+BTXkFtKnixPj1xsHXTVPBNV5YmryJ2V2Z1VOcJy4vv6Istoj0FABySv87cn3oIiNp+N/yiiKrKAk9dRntdUlOGeH75PRnvf0ISVuaJqirCS0l6e2/4qk1dTDKsken4B0PCSvDgmVoi49c+zGsoojWXkalY2m57FoRHV/XyLHmrlUBtLKU1l+dySPNxnUwncikIzpBpqLgGoVCqzoJBOZ+ILjU7HRWc8wixgMvFm4MXEHdz9o8cAfrFYLBqdARCJRAKBQCKRsv7x9P/8DWRmZhEfIf0GhlOu99hQUP4AAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/4a87770169419a3eb059f7414d1fe150/c54d4/css-flexbox.webp 175w, /static/4a87770169419a3eb059f7414d1fe150/a3432/css-flexbox.webp 350w, /static/4a87770169419a3eb059f7414d1fe150/426ac/css-flexbox.webp 700w, /static/4a87770169419a3eb059f7414d1fe150/5304f/css-flexbox.webp 1018w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/4a87770169419a3eb059f7414d1fe150/4edbd/css-flexbox.png 175w, /static/4a87770169419a3eb059f7414d1fe150/13ae7/css-flexbox.png 350w, /static/4a87770169419a3eb059f7414d1fe150/8c557/css-flexbox.png 700w, /static/4a87770169419a3eb059f7414d1fe150/3dde1/css-flexbox.png 1018w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/4a87770169419a3eb059f7414d1fe150/8c557/css-flexbox.png" alt="CSS Flexbox" title="CSS Flexbox" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Flexbox is a powerful layout tool, which allows you to automatically arrange and size items inside a container.</p> <h2 id="flexbox" style="position:relative;"><a href="#flexbox" aria-label="flexbox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flexbox</h2> <p>Flexbox, short for Flexible Box, is defined in the <a href="https://www.w3.org/TR/css-flexbox-1/">CSS Flexible Box Layout Module specifiation</a>.</p> <p>It allows you to control the layout in a parent container. It offers some useful settings, which allow you to control the alignment of the items and to distribute the extra space. Flexbox is one-dimensional. That means it lays out its items in one dimension - either horizontally or vertically. An example of a two-dimensional layout can be CSS Grid.</p> <p>Flexbox is a powerful tool, which allows you to control layout in a way that was previously very difficult or possible only using hacky solutions.</p> <h2 id="flex-container" style="position:relative;"><a href="#flex-container" aria-label="flex container permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex container</h2> <p>With Flexbox, we have to consider both the element itself and also its children. That's because Flexbox is mainly focused on how its children (flex items) are positioned within the main element (flexbox container).</p> <div class="gatsby-code-button-container" data-toaster-id="32741836849783000000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<div class=&quot;flexbox-container&quot;> <div class=&quot;flexbox-item&quot;>1</div> <div class=&quot;flexbox-item&quot;>2</div> <div class=&quot;flexbox-item&quot;>3</div> <div class=&quot;flexbox-item&quot;>4</div> <div class=&quot;flexbox-item&quot;>5</div> </div>`, `32741836849783000000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-container<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flexbox-item<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>5<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre></div> <p>It is important to grasp the concept of <code class="language-text">flexbox-container</code> and <code class="language-text">flexbox-items</code>. That's because some properties for Flexbox are applied to the container level and some for the individual items.</p> <p>Without any CSS styles, the code above will display all the <code class="language-text">flexbox-items</code> under each other. Each item will span the whole width available. That's because <code class="language-text">div</code> is a block element, and it is how block elements behave.</p> <p>Let's try to turn the <code class="language-text">flexbox-container</code> to Flexbox. All we have to do is to add the following style:</p> <div class="gatsby-code-button-container" data-toaster-id="31662222413168360000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.flexbox-container { display: flex; }`, `31662222413168360000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.flexbox-container</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Now the layout will be different. All the items are now displayed horizontally, from left to right. I've also added some background colors and borders, so you can better see the layout.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/ExVPBzM/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>The <code class="language-text">display: flex;</code> property is always applied on the container level, not the item level. It defines that the container should layout its children using Flexbox.</p> <h3 id="block-vs-inline-flexbox" style="position:relative;"><a href="#block-vs-inline-flexbox" aria-label="block vs inline flexbox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Block vs. inline Flexbox</h3> <p>Flexbox is slightly different from positioning normal elements using <code class="language-text">display</code> property. If we use <code class="language-text">display: block;</code> or <code class="language-text">display: inline;</code> we define how the element should be displayed in the document flow.</p> <p>When we defined <code class="language-text">display: flex;</code> we saw that it affected how the children are displayed in the <code class="language-text">flex-container</code>. But what about the container itself - how is it displayed? Is it <code class="language-text">inline</code> element, or is it <code class="language-text">block</code>?</p> <div class="msg-info"> <strong>Block elements:</strong> <ul> <li>Block elements span the whole width available</li> <li>Block elements are positioned one under another</li> <li>Block elements can have properties such as width and height</li> <li>Examples: div, h1, ul</li> </ul> <strong>Inline elements:</strong> <ul> <li>Inline elements are positioned on a line next to each other, from left to right</li> <li>Inline elements take only as much space as required by their contents</li> <li>Inline elements ignore properties such as width and height</li> <li>Inline-block elements behave as inline elements, but you can also specify properties such as width and height</li> <li>Inline elements example: span, b, i, label </li> </ul> </div> <p>Actually, we have two options when using Flexbox. We can make the container either inline or block element using:</p> <ul> <li><code class="language-text">display: flex;</code></li> <li><code class="language-text">display: inline-flex;</code></li> </ul> <p>If we want to our container to be displayed inline, we use <code class="language-text">display: inline-flex;</code>. If we want it to be displayed as a block, we use <code class="language-text">display: flex;</code>.</p> <p>Note that <code class="language-text">display: inline-flex;</code> behaves actually as <code class="language-text">inline-block</code> rather than <code class="language-text">inline</code>. That means it is still displayed on a line, but you can define properties such as <code class="language-text">width</code> and <code class="language-text">height</code>.</p> <p>Be aware that this defines only how the container should be displayed in the document flow, not its child items.</p> <h3 id="flex-direction" style="position:relative;"><a href="#flex-direction" aria-label="flex direction permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex direction</h3> <p>In our example, the child items were displayed horizontally, from left to right. That's the default behavior, if you don't specify otherwise.</p> <p>You can control the flow direction of flex items by defining <code class="language-text">flex-direction</code>. This is done on the container level, not the item level.</p> <ul> <li><code class="language-text">row</code> (default, horizontal, from left to right) </li> <li><code class="language-text">row-reverse</code> (horizontal, from right to left)</li> <li><code class="language-text">column</code> (vertical, from top to bottom)</li> <li><code class="language-text">column-reverse</code> (vertical, from bottom to up)</li> </ul> <p>When we did not specify <code class="language-text">flex-direction</code>, it used the default value, which is <code class="language-text">row</code>. For horizontal placement, but from the other direction, you can use <code class="language-text">row-reverse</code>. For vertical, you can use either <code class="language-text">column</code> or <code class="language-text">column-reverse</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="18619767219128990000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.flex-conteiner { display:flex; flex-direction: column-reverse; }`, `18619767219128990000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.flex-conteiner</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span>flex<span class="token punctuation">;</span> <span class="token property">flex-direction</span><span class="token punctuation">:</span> column-reverse<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/BaoKONW/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <div class="msg-warn"> If you use <i>row-reverse</i> or <i>column-reverse</i>, be aware that this has some usability and accessibility implications. This only changes the visual order of the elements, not their logical order. Screen readers will still process the items in the order they are declared in your HTML. The same applies for navigation using <i>Tab</i> key. </div> <p>We said that <code class="language-text">row</code> means horizontally, left to right. It is not entirely true, though. It applies in languages, which have <a href="https://www.w3.org/TR/css-writing-modes-4/#writing-mode">writing order</a> from left to right. In languages, which use the opposite order (right to left), the Flexbox <code class="language-text">row</code> setting would follow that direction. This means that in languages, such as Arabic, <code class="language-text">flex-direction: row;</code> would actually order items from right to left instead of left to right.</p> <h3 id="main-vs-cross-axis" style="position:relative;"><a href="#main-vs-cross-axis" aria-label="main vs cross axis permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Main vs. cross axis</h3> <p>To better understand some of the following concepts, we need to introduce some more terminology. Flexbox uses two axes. The <code class="language-text">main</code> axis and the <code class="language-text">cross</code> axis. They are perpendicular to each other.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/75ce723a505270a9535b98b1482fe659/41870/flexbox-axis.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 30.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsSAAALEgHS3X78AAAA3ElEQVQY03XPXU/CMBTG8X7/78ON0Rujjo0NfMGXxAhaOtZliGJCQNb176F3unrxa56cPD1ple9avDtEfCMH61WNrQxGz9l8rGT2X1/ILtXV1/gyxS+zPplT57SLhPXjGdg83i2FSfDNPaqtJjg9xC0ykfZ0JmP7coHOByHHOr4cycKUg71D7U3BbnbJbn4VtX9N+Ho+p5mehtzryF17e0JZDPicZShf38ByJPK4qgivaHUacqwTfqcTaB5QrpnSVbkYi+IXJ3w9Yfs2ZCPfRrL70znydow/7nh/4geRs7twOyLZWgAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/75ce723a505270a9535b98b1482fe659/c54d4/flexbox-axis.webp 175w, /static/75ce723a505270a9535b98b1482fe659/a3432/flexbox-axis.webp 350w, /static/75ce723a505270a9535b98b1482fe659/426ac/flexbox-axis.webp 700w, /static/75ce723a505270a9535b98b1482fe659/c139f/flexbox-axis.webp 1050w, /static/75ce723a505270a9535b98b1482fe659/7f403/flexbox-axis.webp 1400w, /static/75ce723a505270a9535b98b1482fe659/d822c/flexbox-axis.webp 1788w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/75ce723a505270a9535b98b1482fe659/4edbd/flexbox-axis.png 175w, /static/75ce723a505270a9535b98b1482fe659/13ae7/flexbox-axis.png 350w, /static/75ce723a505270a9535b98b1482fe659/8c557/flexbox-axis.png 700w, /static/75ce723a505270a9535b98b1482fe659/e996b/flexbox-axis.png 1050w, /static/75ce723a505270a9535b98b1482fe659/2cefc/flexbox-axis.png 1400w, /static/75ce723a505270a9535b98b1482fe659/41870/flexbox-axis.png 1788w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/75ce723a505270a9535b98b1482fe659/8c557/flexbox-axis.png" alt="Flexbox Axis" title="Flexbox Axis" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <ul> <li>The main axis is horizontal in row mode and vertical in column mode</li> <li>The cross axis is vertical in row mode and horizontal in column mode</li> </ul> <p>The direction of each axis depends on the writing mode (left to right in English) and <code class="language-text">flow-direction</code>.</p> <ul> <li>Main axis is left-to-right in <code class="language-text">row</code> and right-to-left in <code class="language-text">row-reverse</code></li> <li>Cross axis is top-to-bottom in <code class="language-text">column</code> and bottom-to-top in <code class="language-text">column-reverse</code></li> </ul> <h3 id="justify-content" style="position:relative;"><a href="#justify-content" aria-label="justify content permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Justify content</h3> <p>In our previous examples, all the items were aligned to the <code class="language-text">flex-start</code> - that is to the left when displaying as a row and to the top when displaying as a column. This is the default behavior, but you can change it with <code class="language-text">justify-content</code> property. For alignment, you can use the following values:</p> <ul> <li><code class="language-text">flex-start</code> (default)</li> <li><code class="language-text">flex-end</code></li> <li><code class="language-text">center</code></li> </ul> <p>It allows you to <strong>align items on the main flex axis</strong>, that is:</p> <ul> <li>horizontally when the <code class="language-text">flex-direction</code> is <code class="language-text">row</code> or <code class="language-text">row-reverse</code></li> <li>vertically when the <code class="language-text">flex-direction</code> is <code class="language-text">column</code> or <code class="language-text">column-reverse</code></li> </ul> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/YzyqOdZ/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Because you can have either normal or reversed flex order, you can't specify alignment as left, right, top, or bottom. Instead, you work with <code class="language-text">flex-start</code> and <code class="language-text">flex-end</code>, which depends on whether you are in normal or reverse mode.</p> <h3 id="distributing-extra-space" style="position:relative;"><a href="#distributing-extra-space" aria-label="distributing extra space permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Distributing extra space</h3> <p>The <code class="language-text">justify-content</code> property can be used not only for aligning items, but also for spreading items evenly across all the available space on the main axis.</p> <p>You can use these valus:</p> <ul> <li><code class="language-text">justify-content: space-between;</code> </li> <li><code class="language-text">justify-content: space-around;</code></li> <li><code class="language-text">justify-content: space-evenly;</code> </li> </ul> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/wvKGYvx/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>In all cases, the items are distributed, so the space between neighboring items is always the same. <strong>The three values differ only in a way how the spacing between first and last item and the edge of the container is calculated</strong>.</p> <ul> <li><code class="language-text">space-between;</code>: first and last items have no space relative to the edge of the container </li> <li><code class="language-text">space-around;</code>: half of the space compared to the gaps between items</li> <li><code class="language-text">space-evenly;</code>: same space as between items </li> </ul> <h2 id="align-items" style="position:relative;"><a href="#align-items" aria-label="align items permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Align items</h2> <p>We learned how to align items on the main axis using <code class="language-text">justify-content</code>. What about the other axis, the <code class="language-text">cross</code> axis?</p> <p>The cross axis is the axis perpendicular to the main axis, that means:</p> <ul> <li>in <code class="language-text">row</code> mode, the cross axis is vertical</li> <li>in <code class="language-text">column</code> mode, the cross axis is horizontal</li> </ul> <p>You can use <code class="language-text">align-items</code> property on the container level to define cross-axis alignment. There are several properties you can choose from:</p> <ul> <li> <p><code class="language-text">flex-start</code>: align to the start of the cross axis</p> <ul> <li>top in row mode</li> <li>left in column mode</li> </ul> </li> <li> <p><code class="language-text">flex-end</code>: align to the end of the cross axis</p> <ul> <li>bottom in row mode</li> <li>right in column mode</li> </ul> </li> <li><code class="language-text">center</code>: align to the middle</li> <li><code class="language-text">stretch</code>: stretch the component to fill the available space on the cross axis (respects <code class="language-text">max-width</code> and <code class="language-text">max-height</code> of items)</li> <li><code class="language-text">baseline</code>: items are aligned by their baselines</li> </ul> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/PoPzgEb/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>This works as expected and is self-explanatory, considering what we've already learned. The <code class="language-text">baseline</code> setting is a bit trickier, though. It aligns items by their <a href="https://en.wikipedia.org/wiki/Baseline_(typography)">baselines</a>. The following image from Wikipedia shows this nicely.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/31c6bee5657fe80a62b022f1046f9abd/f3015/typography-baseline-wikipedia.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/UlEQVQY0yVPTWuDQBT0//+DxuRUb9KAJg2BluSi9FKLWBSCIK6oWEz8WDdx3UXt1A67MDtv3ts3CmOMEFKW5feCIAhc162qap5niCB931NKpZTDAs45FDynaVJw4YOapX+I41jX9aIoMNRxHMuy0BmGYQdQyroOTiHEf5eChjzPm7YlaYqPUDEMQ1XXL9vtSlWfNe31cDBM82m1Mne79WZzvd1aSpMkwb7KOI4YMg2DzLL5WtZ5Fl0uuqa9H49ftv1p2/bphPNxPoO/7fc/hIimZk3D7ndFDAMycMbqMKyjqK2qtml83/c8D4vxxwNZJedyiSuFgBt8GkdUfwF4rQmVT6FjCQAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/31c6bee5657fe80a62b022f1046f9abd/c54d4/typography-baseline-wikipedia.webp 175w, /static/31c6bee5657fe80a62b022f1046f9abd/a3432/typography-baseline-wikipedia.webp 350w, /static/31c6bee5657fe80a62b022f1046f9abd/426ac/typography-baseline-wikipedia.webp 700w, /static/31c6bee5657fe80a62b022f1046f9abd/c139f/typography-baseline-wikipedia.webp 1050w, /static/31c6bee5657fe80a62b022f1046f9abd/7f403/typography-baseline-wikipedia.webp 1400w, /static/31c6bee5657fe80a62b022f1046f9abd/26151/typography-baseline-wikipedia.webp 1804w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/31c6bee5657fe80a62b022f1046f9abd/4edbd/typography-baseline-wikipedia.png 175w, /static/31c6bee5657fe80a62b022f1046f9abd/13ae7/typography-baseline-wikipedia.png 350w, /static/31c6bee5657fe80a62b022f1046f9abd/8c557/typography-baseline-wikipedia.png 700w, /static/31c6bee5657fe80a62b022f1046f9abd/e996b/typography-baseline-wikipedia.png 1050w, /static/31c6bee5657fe80a62b022f1046f9abd/2cefc/typography-baseline-wikipedia.png 1400w, /static/31c6bee5657fe80a62b022f1046f9abd/f3015/typography-baseline-wikipedia.png 1804w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/31c6bee5657fe80a62b022f1046f9abd/8c557/typography-baseline-wikipedia.png" alt="Baseline in typography - from Wikipedia" title="Baseline in typography - from Wikipedia" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Let's look at a specific example:</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/GRpqLeW/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>As you can see, the baseline, in this case, is usually the bottom of the first line of text in each item. The bottom here means where most of the regular letters end. There are some letters such as <code class="language-text">p</code> or <code class="language-text">j</code>. that go under the baseline. </p> <p>Note how multiline items and items with no content are handled. You can check more details about the <a href="https://drafts.csswg.org/css-flexbox-1/#flex-baselines">baseline calculation in Flexbox</a>.</p> <h3 id="wrapping" style="position:relative;"><a href="#wrapping" aria-label="wrapping permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Wrapping</h3> <p>So far, our flex container was big enough to fit all of its content properly. But what happens if there are too many items, which do not fit the size of its parent container?</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/ZEbQdev/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>By default, the container tries to shrink the items if possible, but eventually, the items may overflow out of the container. Note that the container tries to shrink items when not enough space is available even though we explicitly set <code class="language-text">width: 50px;</code>. This width is preserved only if there is enough space. We'll learn how to control or disable this behavior a bit later using <code class="language-text">flex-shrink</code>.</p> <p>This may be ok in some cases, but usually, you want more control over how this case is handled. You can also use <code class="language-text">flex-wrap</code> property on the container level to specify how the wrapping of items should be handled. There are three possible values:</p> <ul> <li><code class="language-text">nowrap</code> - (default) one line, may overflow the container</li> <li><code class="language-text">wrap</code> - wrap to multiple lines from top to bottom</li> <li><code class="language-text">wrap-reverse</code> - wrap to multiple in the opposite order from bottom to top</li> </ul> <p>The difference between <code class="language-text">wrap</code> and <code class="language-text">wrap-reverse</code> may be confusing, let's better look at an example.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/BaojgPQ/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h3 id="aligning-wrapped-content" style="position:relative;"><a href="#aligning-wrapped-content" aria-label="aligning wrapped content permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Aligning wrapped content</h3> <p>When items are wrapped in a Flexbox, and you have, therefore, multiple lines, you can control how these lines are aligned on the <strong>cross axis</strong>. You can use property <code class="language-text">align-content</code>.</p> <p>Do not confuse this with <code class="language-text">align-items</code> and <code class="language-text">justify-content</code>.</p> <ul> <li> <p><code class="language-text">justify-content</code>: aligns across the <strong>main</strong> axis</p> <ul> <li>the main axis is horizontal when in row mode</li> <li>the main axis is vertical when in column mode</li> </ul> </li> <li> <p><code class="language-text">align-items</code>: aligns items within a row on <strong>cross</strong> axis </p> <ul> <li>the cross axis is perpendicular to the main axis</li> </ul> </li> <li> <p><code class="language-text">align-content</code>: <strong>aligns wrapped lines</strong> across the <strong>cross</strong> axis</p> <ul> <li>used to align multiple wrapped lines; for regular cross axis aligning, we can use <code class="language-text">align-items</code></li> </ul> </li> </ul> <div class="msg-info">This property has no effect unless you have either <em>flex-wrap: wrap;</em> or <em>flex-wrap: wrap-reverse;</em>.</div> <p>The good news is that the possible values of <code class="language-text">align-content</code> are very similar as with <code class="language-text">justify-content</code>:</p> <ul> <li><code class="language-text">stretch</code> (default)</li> <li><code class="language-text">flex-start</code></li> <li><code class="language-text">flex-end</code></li> <li><code class="language-text">center</code></li> <li><code class="language-text">space-between</code></li> <li><code class="language-text">space-around</code></li> <li><code class="language-text">space-evenly</code></li> </ul> <p>The alignment just works on the other axis, but the behavior is the same.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/OJyNKXW/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h2 id="item-level-properties" style="position:relative;"><a href="#item-level-properties" aria-label="item level properties permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Item level properties</h2> <p>So far, we've covered properties, which are defined on the flex container level. Now let's look into properties, which are defined on the level of individual flex items.</p> <h3 id="aligning-individual-items" style="position:relative;"><a href="#aligning-individual-items" aria-label="aligning individual items permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Aligning individual items</h3> <p>So far, we covered several options for handling alignment:</p> <ul> <li><code class="language-text">justify-content</code>: align items on the main axis</li> <li><code class="language-text">align-items</code>: align items on the cross axis</li> <li><code class="language-text">align-content</code>: align multiple rows when wrapping on the cross axis</li> </ul> <p>All these settings apply for the whole container. It is possible to also handle the alignment of individual items on the <strong>cross</strong> axis. This is defined on the item level, not the container level. It uses property <code class="language-text">align-self</code>. You can use the following values:</p> <ul> <li><code class="language-text">flex-start</code></li> <li><code class="language-text">flex-end</code></li> <li><code class="language-text">center</code></li> <li><code class="language-text">stretch</code></li> <li><code class="language-text">baseline</code></li> </ul> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/vYNKqMw/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Of course, this can be combined with defining alignment on the container level using <code class="language-text">align-items</code>. You can define general alignment for the items on the container level, and then override it for individual items. For example, you can define items to be aligned for the whole container using <code class="language-text">align-items: flex-end;</code> and then change the alignment for individual items using <code class="language-text">align-self</code>.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/yLYJmeP/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h3 id="ordering-items" style="position:relative;"><a href="#ordering-items" aria-label="ordering items permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Ordering items</h3> <p>Items in the flexbox container are not necessarily displayed in the order, in which they are declared in HTML. That is, you can reorder items by assigning them specific <code class="language-text">order</code> property. This is an integer value and defaults to 0 if not specified otherwise. This property is set not on the flex container level, but at the item level, as you want to order individual items.</p> <p>If you want to put items before those, which don't have order specified, you can use negative values.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/ZEbGeRO/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Multiple items can have the same <code class="language-text">order</code> value. Such items belong to the same <em>ordinal group</em>. Items within the same group are then ordered in the same order in which they are declared in HTML.</p> <p>For example, if you have three items with order 1, they will be placed after all the items with the lower order. All these three items with the same order value will then be placed in the same order as in HTML.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/BaoNWGg/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h4 id="visual-not-logical-order" style="position:relative;"><a href="#visual-not-logical-order" aria-label="visual not logical order permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Visual, not logical order</h4> <p>You have to be careful when changing the order in a flexbox container this way. This only changes the visual order of the items, not logical order. In other words, in DOM, the items are still placed in the same order as in HTML. </p> <p>This has several implications. Most notably, any assistive technologies such as screen readers will process the items in their original order. This also applies to keyboard navigation using <kbd>Tab</kbd> key, unless you also explicitly change <code class="language-text">tab-order</code> of your components.</p> <h3 id="flexibility" style="position:relative;"><a href="#flexibility" aria-label="flexibility permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flexibility</h3> <p>One of the most powerful features of Flexbox is the ability to adjust the size of the items inside based on the size of the container. This means:</p> <ul> <li>You can enlarge the items to fill all the available space</li> <li>You can shrink the items to fit when there is not enough space</li> </ul> <p>This is very powerful as all the items are automatically resized when the size of the flex container changes. Moreover, you can define resizing behavior per each item inside the container. For example - some items can grow more, some items less, and some not at all.</p> <h4 id="flex-grow" style="position:relative;"><a href="#flex-grow" aria-label="flex grow permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex-grow</h4> <p>Using this property, you can control how items should grow when there is extra space available. The default value, if not specified otherwise, is <code class="language-text">flex-grow: 0</code>. That means that <strong>items will not grow to fill the extra space on the main axis by default</strong>.</p> <p>However, this can be changed by assigning any numeric value greater than 0. <strong>Negative values are not allowed</strong>.</p> <p>Let's say we assign <code class="language-text">flex-grow: 1;</code> to all items. This means that the extra space will be assigned proportionally to each item in a way that each item gets the same amount of extra space. This is because their <code class="language-text">flex-grow</code> values are the same.</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/bGVwJLg/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>So what does the value actually do?</p> <p><strong>Extra space available is distributed among the components proportionally to their flex-grow values.</strong></p> <p>This means if there are two items, and both have <code class="language-text">flex-grow: 1;</code> they both get 50% of the available space. That's because the ratio of their values is <code class="language-text">1:1</code>.</p> <p>If one item has <code class="language-text">flex-grow: 3</code> and the other one <code class="language-text">flex-grow: 1</code>, the ratio of their values is 3:1, which means he first one will get 3x as much as space (75%) as the second one (25%). If there are 100px of extra space, the first component will get grow by 75px, and the second one will grow by 25px.</p> <p>The common misconception is that ratio, let's say 2:1, means that the first component will end up being twice as big as the second one. But remember, it is about how the extra space is distributed, not the final size. <strong>The ratio of <code class="language-text">flex-grow</code> values determines the percentage of the extra space each item will get, not their final sizes</strong>.</p> <h4 id="values-between-0-and-1" style="position:relative;"><a href="#values-between-0-and-1" aria-label="values between 0 and 1 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Values between 0 and 1</h4> <p>Values greater than 0, but less than 1 have special behavior. <strong>If the sum of all the <code class="language-text">flex-grow</code> values in a container is less than 1, items will still grow, but will not fill the entire container.</strong></p> <p>For example, if we have three items, each with <code class="language-text">flex-grow: 0.25;</code> their sum is <code class="language-text">0.75</code>. This means that the items will grow, but only occupy 75% of the available space, the rest will be empty. </p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/yLYarGj/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>In the example above, we have two items with value <code class="language-text">0.1</code> and one with <code class="language-text">0.4</code>. The sum is <code class="language-text">0.6</code>. That means that only 60% of extra space will be occupied while the items with <code class="language-text">0.1</code> will get 1/6 of space each, and the item with <code class="language-text">0.4</code> will get <code class="language-text">4/6</code> of distributed space.</p> <p>In other words, 10% of extra space will get two items with <code class="language-text">0.1</code> each, 40% will be added to item with <code class="language-text">0.4,</code> and the rest of 40% will stay unoccupied. </p> <h4 id="flex-shrink" style="position:relative;"><a href="#flex-shrink" aria-label="flex shrink permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex-shrink</h4> <p>This property determines how items should be reduced in size if there is not enough space available in the container.</p> <p>Again, it is a <strong>non-negative</strong> numeric value, where 0 means no shrinking at all. The ratio of these values determines how much each item will shrink compared to others. The difference is that the default value is <code class="language-text">1</code>. This means that the items will shrink by default (but they don't grow by default).</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/dyYpLxo/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>We already encountered this with wrapping - items shrink only to certain degree, so they can still fit their content. After that point, these items will overflow unless you specify wrapping.</p> <p>There is one more notable difference between <code class="language-text">flex-grow</code> and <code class="language-text">flex-shrink</code>. In addition to ratios (same as with growing), <strong>the base size of each item is considered when determining how much each item will shrink</strong>. This means even if the <code class="language-text">flex-shrink</code> value is the same, bigger items will shrink more than smaller items.</p> <p>In other words, larger items (which are able to shrink more) will shrink faster than smaller items (which have limited space left for shrinking).</p> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/WNQGBrz/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h4 id="flex-basis" style="position:relative;"><a href="#flex-basis" aria-label="flex basis permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex-basis</h4> <p>When calculating shrinking and growing in a Flexbox, it is important to know the size of each item on the main axis. For example, to calculate the space available for growth, you need to take the size of the flex container minus sizes of all the items. How are the sizes of individual items calculated, though?</p> <ul> <li>You can specify <code class="language-text">width</code> or <code class="language-text">height</code> of items</li> <li>If not specified, the items are sized based on their content</li> </ul> <p>You can also specify <code class="language-text">flex-basis</code> property, which is used for sizing. It can use the same values as <code class="language-text">width</code> or <code class="language-text">height</code>. You can see, that <code class="language-text">width</code> and <code class="language-text">height</code> are two properties for horizontal and vertical sizing. But there is only one <code class="language-text">flex-basis</code>. That's because it controls sizing on the <strong>main axis</strong>. This means if you are in row mode, it controls width. In column mode, it controls the height. So even if you switch between rows and columns dynamically, you can still use the same <code class="language-text">flex-basis</code> property.</p> <p>The behavior of <code class="language-text">flex-basis</code> is the following:</p> <ul> <li>it defaults to <code class="language-text">auto</code></li> <li><code class="language-text">auto</code> uses either specified <code class="language-text">width</code> or <code class="language-text">height</code>. If these are not specified, it uses sizing based on contents of the item</li> <li>it overrides any <code class="language-text">width</code> or <code class="language-text">height</code></li> <li>it respects min/max width and height</li> <li>you can also use <code class="language-text">content</code> value, which uses automatic size based on content (it was not present in the initial spec, and older browsers do not support it)</li> </ul> <h2 id="shorthand-properties" style="position:relative;"><a href="#shorthand-properties" aria-label="shorthand properties permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Shorthand properties</h2> <p>Although you can specify flex properties such as <code class="language-text">flex-grow</code> or <code class="language-text">flex-shrink</code> individually, there are some shorthand properties, which allow you to combine multiple individual properties together. Using shorthand properties is usually a preferred way instead of defining the properties individually.</p> <h3 id="flex-flow" style="position:relative;"><a href="#flex-flow" aria-label="flex flow permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex-flow</h3> <p>This property allows you to combine <code class="language-text">flex-direction</code> and <code class="language-text">flex-wrap</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="67613694716173340000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`flex-flow: flex-direction flex-wrap;`, `67613694716173340000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">flex-flow</span><span class="token punctuation">:</span> flex-direction flex-wrap<span class="token punctuation">;</span></code></pre></div> <p>Of course, you need to provide specific values instead of <code class="language-text">flex-direction</code> and <code class="language-text">flex-wrap</code>:</p> <div class="gatsby-code-button-container" data-toaster-id="62951542406167600000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`flex-flow: column wrap-reverse; /* Equivaluent css without using shorthand flex property: */ flex-direction: column; flex-wrap: wrap-reverse;`, `62951542406167600000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">flex-flow</span><span class="token punctuation">:</span> column wrap-reverse<span class="token punctuation">;</span> <span class="token comment">/* Equivaluent css without using shorthand flex property: */</span> <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span> <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap-reverse<span class="token punctuation">;</span></code></pre></div> <h3 id="flex" style="position:relative;"><a href="#flex" aria-label="flex permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Flex</h3> <p>This property allows you to combine the following individual properties together:</p> <ul> <li><code class="language-text">flex-grow</code></li> <li><code class="language-text">flex-shrink</code></li> <li><code class="language-text">flex-basis</code></li> </ul> <p>It is defined like this:</p> <div class="gatsby-code-button-container" data-toaster-id="57063396543472075000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`flex: flex-grow flex-shrink flex-basis;`, `57063396543472075000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">flex</span><span class="token punctuation">:</span> flex-grow flex-shrink flex-basis<span class="token punctuation">;</span></code></pre></div> <p>You need to substitute <code class="language-text">flex-grow</code>, <code class="language-text">flex-shrink</code> and <code class="language-text">flex-basis</code> with specific values here:</p> <div class="gatsby-code-button-container" data-toaster-id="5370678033827026000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`flex: 1 2 100px; /* Equivaluent css without using shorthand flex property: */ flex-grow: 1; flex-shrink: 2; flex-basis: 100px;`, `5370678033827026000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">flex</span><span class="token punctuation">:</span> 1 2 100px<span class="token punctuation">;</span> <span class="token comment">/* Equivaluent css without using shorthand flex property: */</span> <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span> <span class="token property">flex-shrink</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token property">flex-basis</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span></code></pre></div> <p>There are also several options to specify less than three values.</p> <h4 id="one-value" style="position:relative;"><a href="#one-value" aria-label="one value permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>One value</h4> <p>With one value, you can use:</p> <ul> <li><code class="language-text">flex: auto;</code> - equivalent to <code class="language-text">flex: 1 1 auto;</code></li> <li><code class="language-text">flex: none;</code> - equivalent to <code class="language-text">flex: 0 0 auto</code></li> <li><code class="language-text">flex: [positive number]</code> - defines just <code class="language-text">flex-grow</code>, the rest uses default values; equivalent to <code class="language-text">flex: [positive number] 1 0px;</code></li> </ul> <h4 id="two-values" style="position:relative;"><a href="#two-values" aria-label="two values permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Two values</h4> <p>You can also specify just two values instead of 3. In this case, the first one is always <code class="language-text">flex-grow</code>. The second one depends on the type of value that you provide.</p> <ul> <li><strong>number</strong> (eg. <code class="language-text">flex: 1 1;</code>) is interpreted as <code class="language-text">flex-shrink</code>, flex basis defaults to 0px in this case</li> <li><strong>valid size value</strong> (eg. <code class="language-text">flex: 1 100px</code>) is interpreted as <code class="language-text">flex-basis</code>, flex shrink defaults to 1</li> </ul> <p>Note when using <code class="language-text">flex</code> shorthand,<code class="language-text">flex-basis</code> defaults to 0, if not specified. This is different from the default value of <code class="language-text">flex-basis</code>, which is <code class="language-text">auto</code>.</p> <h4 id="shorthand-or-individual-properties" style="position:relative;"><a href="#shorthand-or-individual-properties" aria-label="shorthand or individual properties permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Shorthand or individual properties?</h4> <p>This shorthand is preferred over defining the properties individually, as <a href="https://drafts.csswg.org/css-flexbox/#flex-grow-property">stated in the spec</a>.</p> <blockquote> <p>Authors are encouraged to control flexibility using the flex shorthand rather than with flex-grow directly, as the shorthand correctly resets any unspecified components to accommodate common uses.</p> </blockquote> <h2 id="firefox-devtools" style="position:relative;"><a href="#firefox-devtools" aria-label="firefox devtools permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Firefox DevTools</h2> <p>If you want to tinker with Flexbox settings, Firefox developer tools offer <a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_Flexbox_layouts">powerful support</a> for Flexbox debugging and visualization. Check it out.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/40dc7083bb90b12455b373fdc1b5881b/0f246/firefox-flex-inspector.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 63.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABZ0lEQVQoz42S3Y6cMAyFef936k21e12pd93RwnQXJgEGQkISx/mtA70btTtHnzBEOY6D3Qz9cGXy96SnaW7b7sY452PXXqVU4zQptffDwBh3Dh9phNhGPs7TxEfaPJOT4rKsgN4Z9AaRcB69R6ycCiFYaxsKOefyIIwZNkyrC7ON3h9r+eTc75xrKJD/Y9w+570y7f1dX/l26Zg2WdtibDamonU2Om8mC5tqdsRqtuC+/WSvF/nytr38OrjI7z9un284tGloY312lf49MZbTUehhzoW+wKEFD847/Av6QPkfOW9IhVfz6soMxYayuCJceVL6UANc607CYi3bcXH5K53/iPqitWlgN2ZVu5BKqAD45MmUJaXUaPJu4kx5LH15do4xLstCxuZ+Xxljt5FLpWJOIdYR+D80KkAdoj5rF+QKcNtRgP2wso+5pBhSjP+EqgNAY2xD75tQalV2t8WX5J+6M4Cjsf8Dt9vxSkKzclIAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/40dc7083bb90b12455b373fdc1b5881b/c54d4/firefox-flex-inspector.webp 175w, /static/40dc7083bb90b12455b373fdc1b5881b/a3432/firefox-flex-inspector.webp 350w, /static/40dc7083bb90b12455b373fdc1b5881b/426ac/firefox-flex-inspector.webp 700w, /static/40dc7083bb90b12455b373fdc1b5881b/c139f/firefox-flex-inspector.webp 1050w, /static/40dc7083bb90b12455b373fdc1b5881b/34186/firefox-flex-inspector.webp 1118w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/40dc7083bb90b12455b373fdc1b5881b/4edbd/firefox-flex-inspector.png 175w, /static/40dc7083bb90b12455b373fdc1b5881b/13ae7/firefox-flex-inspector.png 350w, /static/40dc7083bb90b12455b373fdc1b5881b/8c557/firefox-flex-inspector.png 700w, /static/40dc7083bb90b12455b373fdc1b5881b/e996b/firefox-flex-inspector.png 1050w, /static/40dc7083bb90b12455b373fdc1b5881b/0f246/firefox-flex-inspector.png 1118w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/40dc7083bb90b12455b373fdc1b5881b/8c557/firefox-flex-inspector.png" alt="Firefox Flexbox inspector" title="Firefox Flexbox inspector" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="browser-support" style="position:relative;"><a href="#browser-support" aria-label="browser support permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Browser Support</h2> <p>Fortunately, Flexbox currently has <a href="https://caniuse.com/#feat=flexbox">great support</a> across all the major browsers. That is 98.72% of all the users. It is even supported by IE 11, even though <a href="https://github.com/philipwalton/flexbugs">it has many issues</a> and some non-standard behavior.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/c1c14b665ce6aca87df640aa0347246c/aec6c/flexbox-support.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 20%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/0lEQVQI1wH0AAv/AOng1unh1+jf0ebezsPVq73Tp7vVsrnVsergz+zf0L7SrbfPp+rayfDbzejd0ebc0eXXyuLUxunb0+fY0gDPzJnGyJNyuHFptmxSr1dJr1JMrlZLsFe8yqfBzKtSr1lFrVCsxJu6yaW1yKW1yaa0x6K2x6N5uHdxuXMAzs6EyMuBoseUnMeSXrRjV7RfaLVpZLdnXLNhWLVfbbhucLtxoMaUp8qap8iYp8qZp8iZqMqbnsaTo8qXAPjs4vjs4v/u4Pzs3cHcsrbZqoHLgXnJe7naq73bru3p0vPq1vzt3f7t3v7t3v7t3v7t3v7t3v/t4P7t30lKrM3R+WVRAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/c1c14b665ce6aca87df640aa0347246c/c54d4/flexbox-support.webp 175w, /static/c1c14b665ce6aca87df640aa0347246c/a3432/flexbox-support.webp 350w, /static/c1c14b665ce6aca87df640aa0347246c/426ac/flexbox-support.webp 700w, /static/c1c14b665ce6aca87df640aa0347246c/c139f/flexbox-support.webp 1050w, /static/c1c14b665ce6aca87df640aa0347246c/7f403/flexbox-support.webp 1400w, /static/c1c14b665ce6aca87df640aa0347246c/074db/flexbox-support.webp 2491w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/c1c14b665ce6aca87df640aa0347246c/4edbd/flexbox-support.png 175w, /static/c1c14b665ce6aca87df640aa0347246c/13ae7/flexbox-support.png 350w, /static/c1c14b665ce6aca87df640aa0347246c/8c557/flexbox-support.png 700w, /static/c1c14b665ce6aca87df640aa0347246c/e996b/flexbox-support.png 1050w, /static/c1c14b665ce6aca87df640aa0347246c/2cefc/flexbox-support.png 1400w, /static/c1c14b665ce6aca87df640aa0347246c/aec6c/flexbox-support.png 2491w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/c1c14b665ce6aca87df640aa0347246c/8c557/flexbox-support.png" alt="Flexbox browser support" title="Flexbox browser support" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>If you are targeting some old browsers, you can check <a href="https://dev.opera.com/articles/advanced-cross-browser-flexbox/">Advanced Cross-Browser Flexbox</a>.</p> <h2 id="learning-flexbox-with-zombies" style="position:relative;"><a href="#learning-flexbox-with-zombies" aria-label="learning flexbox with zombies permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Learning flexbox with zombies</h2> <p>Learning Flexbox is way more fun if there are zombies involved. Instead of reading boring tutorials, you can shoot zombies with your crossbow, while learning Flexbox. Try <a href="https://mastery.games/flexboxzombies/">Flexbox Zombies</a> browser game. It is for free.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/7d2156f54b38e59d7f48a16904a62132/b5c8e/flexbox-zombies.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 54.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACmklEQVQozw3L+08SAQDA8VMQZ9rWcwKpNAVFuwA5HncH3HEH8gaBAxVOHsI8d3cKyEvwNNF8p7a2Hmsm1cqW/VK/tNXWWn9abt99fvsC01Axomap0aWQapFSMclxnh5jY0omrmLnVGx0hImNLCWVHD3K5XQr9DgbVzKJMTalXs5ri0AeqWwaiidI/QSpvkVrK2g9C5dben5jkq9q+Ba8KlgqFahYJ2q1iHD959UFRr2cUa9wuhKwhq23SSEJx54T/LE5xxiTV47WN1L4TAjfHcLPSvsy++Jf5eJvtf2Le/MhtMfri4J17Rypn8FVQHBsJ6EEqrc8dbAhnW/btvTFvnGGNw6w5kfP1tX8yafI3p/Ny6/U/u9K+11I4KCsFi0fEcJrvAEsEE/8aBYH/RmMh0By1pwru7dq3lbtWv9OM3a0x7w6LZw340dnlfdrU4Xoo5gSTK/bqhXvLjCDC16cM0PhhG/LY18Ik6W0/zAdOpgP7mcjh6WZ48bs6bPCRTG4v5N7WZ85dRjLRmXqh5GrOjcBN8zbHLTLSmFgKmBdpIjVOVcz6RFoXytLNhPqmHUAIwdxTG7BpKhNBsP9JuS+wStFmAkaIExphk7t1rddhjACxi2aqAWcxjRh04TPPuSF5Vbgxs3bUqXkjqyz71ZHdx/Q1QOIJT29d6NjccBsogmfi82GUdjtgmgfQhOGOAlRCOixDzhxGQqIu8QiiVjcfZ1IJOkUSYCOzt7ee96RKICCOQ28aEQy4GRmCqu58KrfXvZjRRJl8WHKp3AND0AqOTQs1ysfGEZlerVcr5LpHg/BAUUQIMc5l7HhNJRt2qJFW7BpyxZNAdUs2yZLyEQeU4SwoWniIUUo5ojBqEfuCfZPBaRuShZwSj3/AbIbzB0fSaX/AAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/7d2156f54b38e59d7f48a16904a62132/c54d4/flexbox-zombies.webp 175w, /static/7d2156f54b38e59d7f48a16904a62132/a3432/flexbox-zombies.webp 350w, /static/7d2156f54b38e59d7f48a16904a62132/426ac/flexbox-zombies.webp 700w, /static/7d2156f54b38e59d7f48a16904a62132/c139f/flexbox-zombies.webp 1050w, /static/7d2156f54b38e59d7f48a16904a62132/7f403/flexbox-zombies.webp 1400w, /static/7d2156f54b38e59d7f48a16904a62132/3b495/flexbox-zombies.webp 2194w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/7d2156f54b38e59d7f48a16904a62132/4edbd/flexbox-zombies.png 175w, /static/7d2156f54b38e59d7f48a16904a62132/13ae7/flexbox-zombies.png 350w, /static/7d2156f54b38e59d7f48a16904a62132/8c557/flexbox-zombies.png 700w, /static/7d2156f54b38e59d7f48a16904a62132/e996b/flexbox-zombies.png 1050w, /static/7d2156f54b38e59d7f48a16904a62132/2cefc/flexbox-zombies.png 1400w, /static/7d2156f54b38e59d7f48a16904a62132/b5c8e/flexbox-zombies.png 2194w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/7d2156f54b38e59d7f48a16904a62132/8c557/flexbox-zombies.png" alt="Flexbox Zombies" title="Flexbox Zombies" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Want some more practice? You can check other games helping you to master Flexbox, such as <a href="http://www.flexboxdefense.com/">Flexbox Defense</a> - a tower defense game where you position your towers using CSS and Flexbox. Another one is <a href="https://flexboxfroggy.com/">Flexbox Froggy</a> - help Froggy and his friends to reach lilypads and cross a pond by writing CSS code.</p> <h2 id="additional-resources" style="position:relative;"><a href="#additional-resources" aria-label="additional resources permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Additional resources</h2> <ul> <li><a href="https://www.w3.org/TR/css-flexbox-1/">Flexbox specification</a></li> <li><a href="https://philipwalton.github.io/solved-by-flexbox/">Solved by Flexbox</a> - showcase of common layout problems solved by Flexbox</li> <li><a href="https://flexbox.help/">Interactive Flexbox playground for testing various flex properties</a></li> </ul> <!-- TODO cheatsheet --> <!-- TODO table showing which are on parent level vs. on item level ..> https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Developer Advocate Book Review]]>https://www.vojtechruzicka.com/developer-advocate-book-review/https://www.vojtechruzicka.com/developer-advocate-book-review/Mon, 20 Apr 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/6327d0398407d2a00fb71822e622d64f/d2a60/developer-advocate-book-review.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 75.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAIAAABr+ngCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC9UlEQVQozz1TaU9TQRTtXzDBoEDL0r5l3ry9rxRKd/po6cbWiqDsJVoNtSyCMTHGRIUY8YPfNXGNiZVC2BeVgIb4rzwPicnNZObO3HvOufeOjQgaL2hE0BubCU9Ugeo4yorXxcq4EkWD4WSsTpcIv4uTGVaWlVZC3aLksbFErXewI2OFlZXVZKrfNNOhUFdnLNURiI1P3BocHMEmnugJReI+f6c/ZEY7k4riFaibSoaN4ZWaWsez5y/ef/h8evp7Z3e/Uqmub2z+PD6BZ2t7d21tY3Nze3fv8Pj4ZK268ebtO93dTqiOeAvZ0cxnevOT08WomZyYKsa60tm+axOFYio7MDg8duPm5Oqr1xNTt68Pj4+MFtLZXDLVx3GKFUwlDxTWO5hAuCsQMuEaHZtG4haXKEhGo5PIqldSWtt8EX8gBuWI9PkiKARFsCS3coIGAXv7hweH379Wqicnv0AYVL+trW9u7UDRwcHR4dGPyrfq2dmfj5++hMNxnmgAsLlYCbQNb7B4t1yeXVxefjkzMzc7u1Qs3lt68KhUXpiavvP4ydNSab5UWlhYfNifGwKjFkZqYUQEy5DNcEq9nalrcNkbObuDbbAzWHG8dLmhprax9mqT3W4dr9Q5gcQRleUV8LVBpCVAdOtauyh7wtFEMt2fSPVle/KxeMaMZ9KZgUR3byabwxVPNUVt0/BS8mBjo6KBNJgHSTI4orX5wpFoIhiJhyIJfzCG3gbDcVgk2h0ImjzVASBJHl7QL/oMZIwOaLhAhletmaO6k7VUWSPFK5itJqcAwv9mA/tmF4VZBQOygDEkChHdOPr80aHh8UxPPtub7073pbI5FAksOEHHFOMBMlpRRAWy2uSkINyCyvEqXCghoJAOuTDDeAo0dBQet+HXjQ5Za0PnKTVslMimmUh1Z1LJTNxMEKJyF2Z9A3r+MbjzzwND5WZK8+W5+6NjBUF020RBliXVUD0evRUrIQrwodbJwiTe4in/1w8nmgxkVW9Hrr9SM+26Z+/NPQAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/6327d0398407d2a00fb71822e622d64f/c54d4/developer-advocate-book-review.webp 175w, /static/6327d0398407d2a00fb71822e622d64f/a3432/developer-advocate-book-review.webp 350w, /static/6327d0398407d2a00fb71822e622d64f/426ac/developer-advocate-book-review.webp 700w, /static/6327d0398407d2a00fb71822e622d64f/9ec5a/developer-advocate-book-review.webp 807w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/6327d0398407d2a00fb71822e622d64f/4edbd/developer-advocate-book-review.png 175w, /static/6327d0398407d2a00fb71822e622d64f/13ae7/developer-advocate-book-review.png 350w, /static/6327d0398407d2a00fb71822e622d64f/8c557/developer-advocate-book-review.png 700w, /static/6327d0398407d2a00fb71822e622d64f/d2a60/developer-advocate-book-review.png 807w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/6327d0398407d2a00fb71822e622d64f/8c557/developer-advocate-book-review.png" alt="Developer Advocate Book Review" title="Developer Advocate Book Review" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Book review of Developer, Advocate! by Geertjan Wielenga. A collection of conversations with developer advocates about their work, tech communities and the road to becoming a developer advocate.</p> <h2 id="about-the-author" style="position:relative;"><a href="#about-the-author" aria-label="about the author permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>About the author</h2> <p><a href="https://twitter.com/GeertjanW">Geertjan Wielenga</a> is a product manager and developer advocate for open source projects in Oracle, such as Oracle JET and Apache NetBeans.</p> <p>You can check his <a href="https://blogs.oracle.com/author/geertjan-wielenga">Oracle blog</a>, <a href="https://github.com/GeertjanWielenga">GitHub</a> and <a href="https://www.youtube.com/user/GeertjanWielenga1/videos">Youtube channel</a>. The list of his other books is available <a href="https://www.amazon.com/s?i=stripbooks&#x26;rh=p_27%3AGeertjan+Wielenga&#x26;s=relevancerank&#x26;text=Geertjan+Wielenga&#x26;ref=dp_byline_sr_book_1">here</a>.</p> <h2 id="the-concept" style="position:relative;"><a href="#the-concept" aria-label="the concept permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The concept</h2> <p>The concept of the book is simple. In a nutshell, each chapter is an interview with a well know developer advocate. After a brief introduction of the person, there is a series of questions. While some of the questions are specific to each person, many of them tend to repeat. Such as how did they become developer advocates, what advice would they give to people who want to become advocates themselves. How to deal with burnout and frequent traveling, technical problems or difficult questions.</p> <h2 id="the-advocates" style="position:relative;"><a href="#the-advocates" aria-label="the advocates permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The advocates</h2> <p>The book contains interviews with 32 developer advocates in total. Here are some examples.</p> <ul> <li><a href="https://www.hanselman.com/">Scott Hanselman</a></li> <li><a href="https://twitter.com/mkheck">Mark Heckler</a></li> <li><a href="https://twitter.com/arungupta">Arun Gupta</a></li> <li><a href="https://trishagee.github.io/">Trisha Gee</a></li> <li><a href="https://joshlong.com/">Josh Long</a></li> <li><a href="http://adam-bien.com/">Adam Bien</a></li> <li><a href="https://twitter.com/javaperftuning">Kirk Pepperdine</a></li> <li><a href="https://twitter.com/venkat_s">Venkat Subramaniam</a></li> </ul> <p>You can check <a href="https://gist.github.com/vojtechruz/db120f75b463adb390e06a17b4999a33">the full list</a> of developer advocates interviewed in the book. </p> <h2 id="verdict" style="position:relative;"><a href="#verdict" aria-label="verdict permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Verdict</h2> <p>The book is definitely a very interesting insight into the life and role of a developer advocate. Considering there are 32 advocates interviewed, you can read a lot of different stories and backgrounds. It is interesting to get to know more people from the community. Since the advocates have different backgrounds, it allows you to learn about interesting people, which are not inside of your usual "bubble" defined by your technology stack.</p> <p>If you want to get involved more in the community, this book is definitely a great source of inspiration as you can read about the journey of seasoned advocates from the bottom-up - their humble beginnings in small user groups up to the huge conferences. It shows that even you can aim to become an advocate - you don't necessarily have to know everything about a certain topic. It just takes time and dedication, but you can eventually get there and the advocates share some useful tips on how to get started.</p> <p>Since every chapter is independent, you don't have to read it from cover to cover - you can pick people you are interested in or continue later as there is no connection between individual chapters.</p> <p>One thing I didn't like that much is that a lot of questions tend to repeat. In some cases, it makes sense. In others, not so much. It can be tedious to read over and over the same questions with very similar answers, such as: </p> <ul> <li>What is the difference between developer evangelist and developer advocate</li> <li>What do you do if you don't know an answer to a question</li> <li>What do you do if your equipment does not work properly</li> </ul> <p>And some more. The answers are very similar in these cases and it would be sufficient to ask it once or twice. I would be much more interested in more personal details about the advocates, their careers and topics which make them unique rather than all this generic stuff.</p> <p>Also, the book is quite long, with nearly 800 pages and as I mentioned, a lot of topics that repeat. It would be easier to digest, maybe as an audiobook or even better - as a series of individual podcasts, where you can actually hear dialog between the author and each advocate.</p> <p>Overall, I think it is an interesting book worth reading, which can greatly motivate you, especially if you intend to get involved more.</p> <h2 id="additional-resources" style="position:relative;"><a href="#additional-resources" aria-label="additional resources permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Additional Resources</h2> <ul> <li><a href="https://www.packtpub.com/business-other/developer-advocate">Buy the book from Packt</a></li> <li><a href="https://www.amazon.com/Developer-Advocate-Conversations-turning-passion/dp/1789138744">Buy the book from Amazon</a></li> <li><a href="https://www.amazon.com/Developer-Advocate-Conversations-turning-passion/dp/1789138744#customerReviews">Amazon user reviews</a></li> <li><a href="https://www.goodreads.com/book/show/48574066-developer-advocate">Goodreads book reviews</a></li> <li><a href="https://www.infoq.com/articles/developer-advocate/">Interview with the author on InfoQ</a></li> <li><a href="https://spring.io/blog/2019/10/04/a-bootiful-podcast-oracle-s-geertjan-wielenga-on-his-new-book-developer-advocate">Bootiful podcast about the book</a></li> </ul>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Adjusting your web for printing with CSS]]>https://www.vojtechruzicka.com/css-printing/https://www.vojtechruzicka.com/css-printing/Fri, 10 Apr 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAACA//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAEPQyJcR4//xAAYEAEAAwEAAAAAAAAAAAAAAAABAAIRMf/aAAgBAQABBQKsF23SLg3Z/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRABAAIDAAAAAAAAAAAAAAAAACExARBx/9oACAEBAAY/ArUjGuof/8QAGhABAAIDAQAAAAAAAAAAAAAAAQARIUGRcf/aAAgBAQABPyElCdRCEUxaPY0xcqa9QjlU/9oADAMBAAIAAwAAABCID//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABURAQEAAAAAAAAAAAAAAAAAAAAh/9oACAECAQE/EEf/xAAbEAEAAgMBAQAAAAAAAAAAAAABABEhMVFh4f/aAAgBAQABPxA2YmwWXBACOVvsHAAFbF+xeqw4+xVkKXh7qUVDwZ//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/b83f68a547e74b8ee429318509975685/c54d4/css-printing.webp 175w, /static/b83f68a547e74b8ee429318509975685/a3432/css-printing.webp 350w, /static/b83f68a547e74b8ee429318509975685/426ac/css-printing.webp 700w, /static/b83f68a547e74b8ee429318509975685/c139f/css-printing.webp 1050w, /static/b83f68a547e74b8ee429318509975685/7f403/css-printing.webp 1400w, /static/b83f68a547e74b8ee429318509975685/fad48/css-printing.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/b83f68a547e74b8ee429318509975685/e52aa/css-printing.jpg 175w, /static/b83f68a547e74b8ee429318509975685/70ebb/css-printing.jpg 350w, /static/b83f68a547e74b8ee429318509975685/29d31/css-printing.jpg 700w, /static/b83f68a547e74b8ee429318509975685/9ecec/css-printing.jpg 1050w, /static/b83f68a547e74b8ee429318509975685/d165a/css-printing.jpg 1400w, /static/b83f68a547e74b8ee429318509975685/b17f8/css-printing.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/b83f68a547e74b8ee429318509975685/29d31/css-printing.jpg" alt="CSS Printing" title="CSS Printing" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>When printing a web page, is it important to adjust the layout and the content of your page. Many elements are not relevant for printing, it is vital to properly control page breaks and handle hyperlinks. All of this can be controlled by CSS and you can even trigger printing in Javascript or react to user print action. Let's learn how. </p> <h2 id="adding-print-stylesheet" style="position:relative;"><a href="#adding-print-stylesheet" aria-label="adding print stylesheet permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding print stylesheet</h2> <p>When you want to apply certain CSS rules just when printing, you can easily add a separate stylesheet, which will be used just for printing purposes.</p> <div class="gatsby-code-button-container" data-toaster-id="819356409472393700" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<link rel=&quot;stylesheet&quot; media=&quot;print&quot; href=&quot;printing-stylesheet.css&quot;>`, `819356409472393700`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>print<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>printing-stylesheet.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre></div> <p>If you add your stylesheet this way, it will be applied <strong>in addition</strong> to your regular stylesheet. This is often what you need. However, if you would like to have two distinct stylesheets - one only for the screen, one just for printing, you can achieve it like this:</p> <div class="gatsby-code-button-container" data-toaster-id="18191638513013352000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<link rel=&quot;stylesheet&quot; media=&quot;screen&quot; href=&quot;screen-stylesheet.css&quot;> <link rel=&quot;stylesheet&quot; media=&quot;print&quot; href=&quot;printing-stylesheet.css&quot;>`, `18191638513013352000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="html"><pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>screen<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>screen-stylesheet.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>print<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>printing-stylesheet.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre></div> <p>Having separate stylesheet just for printing can be useful, if you want to <a href="http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml">load it dynamically</a>, only when needed or you prefer your print styles to be completely separated.</p> <h2 id="media-queries" style="position:relative;"><a href="#media-queries" aria-label="media queries permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Media queries</h2> <p>A more modern way of including print-specific CSS is using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries">media queries</a>.</p> <p>You can define that a certain set of CSS rules can be applied only when printing.</p> <div class="gatsby-code-button-container" data-toaster-id="34721877626866360000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@media print { ... }`, `34721877626866360000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> print</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre></div> <p>Or only when using a screen.</p> <div class="gatsby-code-button-container" data-toaster-id="39787784185132670000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@media screen { ... }`, `39787784185132670000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre></div> <p>This approach may be preferred to separate print stylesheet as you can easily have regular and print CSS for a page/component in the same place. This makes the maintenance easier and it is less likely that you will forget to update printing styles when making changes to regular styles.</p> <h2 id="check-your-css-framework" style="position:relative;"><a href="#check-your-css-framework" aria-label="check your css framework permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Check your CSS framework</h2> <p>If you are using external CSS styles, for example, a framework such as Bootstrap, you should first check its print support.</p> <p>Bootstrap, for example, provides some support out of the box - it does hide navigation bar and so on.</p> <h2 id="removing-unnecessary-content" style="position:relative;"><a href="#removing-unnecessary-content" aria-label="removing unnecessary content permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Removing unnecessary content</h2> <p>Web pages contain a lot of content, which is not really useful when printing. This can be:</p> <ul> <li>Navigation bar</li> <li>Social share buttons</li> <li>Media content such as video or audio</li> <li>Cookie and subscription toolbars</li> <li>Ads</li> </ul> <p>And much more. It is useful to remove this content for printing. This way, your users save paper, ink, and the output is easy to read and not cluttered.</p> <p>It is easy to hide elements by setting <code class="language-text">display:none;</code>:</p> <div class="gatsby-code-button-container" data-toaster-id="7984507424433840000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@media print { .navigation { display: none; } }`, `7984507424433840000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> print</span> <span class="token punctuation">{</span> <span class="token selector">.navigation</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>Of course, you can do any adjustments necessary such as reducing size, changing fonts, simplifying graphics - pretty much anything you can do in CSS.</p> <h2 id="page-margins" style="position:relative;"><a href="#page-margins" aria-label="page margins permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Page Margins</h2> <p>When printing, it may be useful to override the default settings for margins of your pages (for example, to provide space for writing notes).</p> <div class="gatsby-code-button-container" data-toaster-id="58962323335520670000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@page { margin: 20mm; }`, `58962323335520670000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@page</span></span> <span class="token punctuation">{</span> <span class="token property">margin</span><span class="token punctuation">:</span> 20mm<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Of course, you can set individual margins separately. This can be useful when printing double-sided documents. That's because when you want to bind your documents together, you need extra space for better readability. However, with double-sided pages, this space is either on the left or right of the page, depending on whether it is odd or even.</p> <p>Fortunately, you can select odd and even pages by <code class="language-text">@page:left</code> and <code class="language-text">@page:right</code>. Left here means left-facing page, and right is right-facing.</p> <div class="gatsby-code-button-container" data-toaster-id="30060127952290406000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@page:left { margin-right: 30mm; } @page:right { margin-left: 30mm; }`, `30060127952290406000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@page</span><span class="token punctuation">:</span>left</span> <span class="token punctuation">{</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> 30mm<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@page</span><span class="token punctuation">:</span>right</span> <span class="token punctuation">{</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> 30mm<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>If you want to treat the first page separately, you can use <code class="language-text">@page:first</code>.</p> <h2 id="page-breaks" style="position:relative;"><a href="#page-breaks" aria-label="page breaks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Page breaks</h2> <p>For better readability, it can be useful to control where page breaks are inserted. </p> <h3 id="avoiding-breaks" style="position:relative;"><a href="#avoiding-breaks" aria-label="avoiding breaks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Avoiding breaks</h3> <p>It can be inconvenient if images or code examples get split across two pages. Fortunately, you can ensure that certain elements will never be split. Let's say we want to do that for images and code blocks:</p> <div class="gatsby-code-button-container" data-toaster-id="89668253857030820000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`img, pre { break-inside: avoid-page; }`, `89668253857030820000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">img, pre</span> <span class="token punctuation">{</span> <span class="token property">break-inside</span><span class="token punctuation">:</span> avoid-page<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>You can also specify that page break should not be inserted before or after an element. This can be useful, for example, so that heading is not placed as the last element on a page and is not separated from the following paragraph.</p> <div class="gatsby-code-button-container" data-toaster-id="74876667102919660000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`h1, h2, h3 { break-after: avoid-page; }`, `74876667102919660000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">h1, h2, h3</span> <span class="token punctuation">{</span> <span class="token property">break-after</span><span class="token punctuation">:</span> avoid-page<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h3 id="explicit-breaks" style="position:relative;"><a href="#explicit-breaks" aria-label="explicit breaks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Explicit breaks</h3> <p>Sometimes, it can be useful to explicitly insert page breaks before or after certain elements.</p> <p>For example, you may need each chapter to start on its own page. You can achieve it using <code class="language-text">break-before</code> on your chapter heading.</p> <div class="gatsby-code-button-container" data-toaster-id="65808590819077240000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`h2 { break-before: page; }`, `65808590819077240000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">h2</span> <span class="token punctuation">{</span> <span class="token property">break-before</span><span class="token punctuation">:</span> page<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>You can also insert page breaks after an element using <code class="language-text">break-after</code>.</p> <div class="msg-info"> There are older properties <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before">page-break-before</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-after">page-break-after</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-inside">page-break-inside</a>, which are now deprecated in favor of <em>break-before</em>, <em>break-after</em> and <em>break-inside</em>.</div> <h3 id="paragraph-splitting" style="position:relative;"><a href="#paragraph-splitting" aria-label="paragraph splitting permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Paragraph splitting</h3> <p>Fortunately, you do have fine-grained control over how paragraphs get split to the next page. We have two properties for this - <code class="language-text">widows</code> and <code class="language-text">orphans</code>. The naming may sound weird, but that's because it <a href="https://en.wikipedia.org/wiki/Widows_and_orphans">originates from typesetting</a>.</p> <h4 id="orphans" style="position:relative;"><a href="#orphans" aria-label="orphans permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Orphans</h4> <p>When a paragraph gets split over two pages, <code class="language-text">orphans</code> property defines how many lines at a minimum should be kept on the first page.</p> <div class="gatsby-code-button-container" data-toaster-id="64754786635190210000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`p { orphans: 4; }`, `64754786635190210000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">orphans</span><span class="token punctuation">:</span> 4<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <h4 id="widows" style="position:relative;"><a href="#widows" aria-label="widows permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Widows</h4> <p>This is very similar to orphans. It is the minimal number of lines, which should be on the <strong>second</strong> page when a paragraph is split on two pages.</p> <div class="gatsby-code-button-container" data-toaster-id="43429572022901694000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`p { widows: 5; }`, `43429572022901694000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">widows</span><span class="token punctuation">:</span> 5<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Because of the unusual naming, it can be difficult to remember the difference between widows and orphans and which is which. You can use the following mnemonic:</p> <blockquote> <p>An orphan starts alone, a widow ends alone.</p> </blockquote> <p>Note that the browser is not 100% guaranteed to follow these rules when printing. It can decide to break this rule in favor of some other printing optimizations.</p> <p>There is a <a href="https://caniuse.com/#feat=css-widows-orphans">good browser support</a> (as of 4/2020) for widows and orphans, 93.76%, except for Firefox.</p> <h2 id="hyperlinks" style="position:relative;"><a href="#hyperlinks" aria-label="hyperlinks permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Hyperlinks</h2> <p>You should take extra care when handling printed hyperlinks. Many pages these days don't use underline for hyperlinks and rather differentiate links by color. This is not very convenient when printing, especially with black and white output. Marking hyperlinks with underline is a good traditional way of recognizing them even when printing.</p> <div class="gatsby-code-button-container" data-toaster-id="32545930498708530000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@media print { a { text-decoration: underline; } }`, `32545930498708530000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> print</span> <span class="token punctuation">{</span> <span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>Another problem with hyperlinks is that in a printed document, there is no way to determine where the link is pointing to. Fortunately, this can be fixed with just a little bit of CSS.</p> <div class="gatsby-code-button-container" data-toaster-id="14231786231688925000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`a:after { content: &quot; (&quot; attr(href) &quot;) &quot;; font-size: 80%; }`, `14231786231688925000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">a:after</span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">" ("</span> <span class="token function">attr</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span> <span class="token string">") "</span><span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>This includes URL after each <code class="language-text">a</code> element, so you can clearly see where it is pointing to. The URL is a bit smaller than the regular text for better readability. Here's how it looks:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 12.571428571428573%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAY0lEQVQI14WLwQrAIAxD/f/PnAyGbVqH6GnTRXfaaSHQNOQFmJVSziU3vAFAdjczBUSEjTIB7rMFjFdVQ0TdvUWj66Y1oh1ez5y5EBXyKSWSSWZeLBshySfcfVxf9/Gv3ufqAU0FqyHuzbAsAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/14b41886e17bf050e9900b44dca1fac5/c54d4/print-links.webp 175w, /static/14b41886e17bf050e9900b44dca1fac5/a3432/print-links.webp 350w, /static/14b41886e17bf050e9900b44dca1fac5/426ac/print-links.webp 700w, /static/14b41886e17bf050e9900b44dca1fac5/c139f/print-links.webp 1050w, /static/14b41886e17bf050e9900b44dca1fac5/7f80f/print-links.webp 1394w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/14b41886e17bf050e9900b44dca1fac5/4edbd/print-links.png 175w, /static/14b41886e17bf050e9900b44dca1fac5/13ae7/print-links.png 350w, /static/14b41886e17bf050e9900b44dca1fac5/8c557/print-links.png 700w, /static/14b41886e17bf050e9900b44dca1fac5/e996b/print-links.png 1050w, /static/14b41886e17bf050e9900b44dca1fac5/d4b10/print-links.png 1394w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/14b41886e17bf050e9900b44dca1fac5/8c557/print-links.png" alt="Printing link URLs" title="Printing link URLs" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>This is good, but we can do even better. The first issue to solve is to make sure only links in text are processed, not all the links on the page. We can prefix the <code class="language-text">a</code> selector in our code with <code class="language-text">p</code>. Or better yet, with the container for our page text, such as <code class="language-text">#page-content</code> (or whatever name you might have).</p> <p>The second improvement can be restricting this to only external links. We can do this by selecting only <code class="language-text">a</code> tags where <code class="language-text">href</code> starts with <code class="language-text">http</code>:</p> <div class="gatsby-code-button-container" data-toaster-id="54076024658075770000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`#page-content a[href^=&quot;http&quot;]:after { content: &quot; (&quot; attr(href) &quot;)&quot;; font-size: 80%; }`, `54076024658075770000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#page-content a[href^="http"]:after</span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">" ("</span> <span class="token function">attr</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span> <span class="token string">")"</span><span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>This solution works, but if you have many links, especially with long URLs, it can become hard to read. There is an alternative solution described by <a href="https://alistapart.com/article/improvingprint/">Aaron Gustafson</a>.</p> <p>The idea is not to put URL after each link, but rather a reference number (1,2,3,...) and then at the end of the document provide a reference table with all the number and their URLs. It also covers cases where a link is present more than once - in such situations the duplicate links share the same number.</p> <p>This also involves a bit of JavaScript, but it can be handy when working with a lot of links. Here's an example of this approach in action:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 59.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABW0lEQVQoz5VS2XKDMAzk/78vHAZ8cITTNncaQiDdmiTNNJOH7oxly0bSaoXVNBNjirE8juM0MYjj4/EIN4qi2IAzGobK81pCvg72iRAdBH2enyytpsMho7QQQnDOOOc4BEFAKQJCz/OSNKnKqiiKNE2iSAqh8jyD23WdNU2D1nU/dFVVKyWVUm3bns/nZVkuBrfPsNZ1FSIK/EBwwRjziV9VlTLo+74sy3Ec8d22bcbezP4IRgX0xhhFCvRMCJFSXa9X1Hzaj5X3581gPzwJLwaXB9DL/DjP84wnaxyGNE3rum6bVutGY70AqkCC3T4vh76Hi3YsOK7rIs3t/7BAFapIKZFFSVnfIfcbUN3VesVvMFaWZdDJ84ht29hAxLYdzDnLc3Sxvur7pzIWKjuO42NKvo+/4ifadZHFuIRShrGBxfIm+71yGGLOHIEYtdIanCHJarALDru9UfgGBB+lfZAu/qsAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/7db8209c294cc496c3eefba6f9f46443/c54d4/print-links-legend.webp 175w, /static/7db8209c294cc496c3eefba6f9f46443/a3432/print-links-legend.webp 350w, /static/7db8209c294cc496c3eefba6f9f46443/426ac/print-links-legend.webp 700w, /static/7db8209c294cc496c3eefba6f9f46443/c139f/print-links-legend.webp 1050w, /static/7db8209c294cc496c3eefba6f9f46443/7f403/print-links-legend.webp 1400w, /static/7db8209c294cc496c3eefba6f9f46443/63e6a/print-links-legend.webp 1653w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/7db8209c294cc496c3eefba6f9f46443/4edbd/print-links-legend.png 175w, /static/7db8209c294cc496c3eefba6f9f46443/13ae7/print-links-legend.png 350w, /static/7db8209c294cc496c3eefba6f9f46443/8c557/print-links-legend.png 700w, /static/7db8209c294cc496c3eefba6f9f46443/e996b/print-links-legend.png 1050w, /static/7db8209c294cc496c3eefba6f9f46443/2cefc/print-links-legend.png 1400w, /static/7db8209c294cc496c3eefba6f9f46443/03bcf/print-links-legend.png 1653w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/7db8209c294cc496c3eefba6f9f46443/8c557/print-links-legend.png" alt="Printing links with a legend" title="Printing links with a legend" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="page-margin-boxes" style="position:relative;"><a href="#page-margin-boxes" aria-label="page margin boxes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Page Margin Boxes</h2> <p>When printing web pages, browsers can include various information in the headers and footers of each page, such as:</p> <ul> <li>Page numbers</li> <li>Page title</li> <li>Origin URL</li> </ul> <p>Currently, you do not have control over this content. Fortunately, this is about to change. There is a specification <a href="https://drafts.csswg.org/css-page-3/#margin-boxes">CSS Paged Media Module Level 3</a>, which addresses this issue. </p> <p>This can be useful for adding information such as copyright, page logo, custom page counters, etc.</p> <p>There are 16 regions, which you can target:</p> <ul> <li>4 corners of the page</li> <li>3 regions at each edge (top, bottom, right, left)</li> </ul> <p>For example, if you want to target the bottom of the page, you can choose from</p> <ul> <li><code class="language-text">bottom-left-corner</code></li> <li><code class="language-text">bottom-right-corner</code></li> <li><code class="language-text">bottom-left</code></li> <li><code class="language-text">bottom-center</code></li> <li><code class="language-text">bottom-right</code></li> </ul> <p>Let's look at a specific example.</p> <div class="gatsby-code-button-container" data-toaster-id="5469993856477307000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@page { @top-center { content: &quot;Vojtech Ruzicka's Programming Blog&quot;; } @top-right-corner { content: url(logo.png); } @bottom-right { content: &quot;Page &quot; counter(page) } }`, `5469993856477307000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@page</span></span> <span class="token punctuation">{</span> <span class="token atrule"><span class="token rule">@top-center</span></span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Vojtech Ruzicka's Programming Blog"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@top-right-corner</span></span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span>logo.png<span class="token punctuation">)</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token atrule"><span class="token rule">@bottom-right</span></span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Page "</span> <span class="token function">counter</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>You can specify not only content, but also additional properties such as fonts or borders:</p> <div class="gatsby-code-button-container" data-toaster-id="4362696036648605700" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@page { @top-center { content: &quot;Vojtech Ruzicka's Programming Blog&quot;; font-family: sans-serif; font-weight: bold; font-size: 2em; } }`, `4362696036648605700`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@page</span></span> <span class="token punctuation">{</span> <span class="token atrule"><span class="token rule">@top-center</span></span> <span class="token punctuation">{</span> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Vojtech Ruzicka's Programming Blog"</span><span class="token punctuation">;</span> <span class="token property">font-family</span><span class="token punctuation">:</span> sans-serif<span class="token punctuation">;</span> <span class="token property">font-weight</span><span class="token punctuation">:</span> bold<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 2em<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>The specification is currently in the Editor's Draft stage (as of 4/2020) and currently <a href="https://caniuse.com/#search=page-mar">no browsers support adding custom headers and footers</a>. Hopefully, this will change in the near future. You can track the corresponding <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=320370">Chrome issue</a>. The older parts of this specification are already implemented (such as setting page margins), but custom content for margin boxes is yet to come.</p> <h2 id="handling-printing-in-js" style="position:relative;"><a href="#handling-printing-in-js" aria-label="handling printing in js permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Handling printing in JS</h2> <p>With JavaScript, you don't have to rely on your users to initiate the printing of your page. You can trigger it yourself using:</p> <div class="gatsby-code-button-container" data-toaster-id="22380730393407844000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`window.print();`, `22380730393407844000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="javascript"><pre class="language-javascript"><code class="language-javascript">window<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>You may be tempted to use your own print button if you need to perform some custom JS logic before printing - such as manipulating DOM, loading new data, and so on.</p> <p>The problem is that you cannot guarantee that users will use your button instead of native browser printing.</p> <p>For such cases, where you need to perform some custom logic before (or even after) printing, there are two handy events.</p> <ul> <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeprint_event">beforeprint</a></li> <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/afterprint_event">afterprint</a></li> </ul> <p>As the name suggests, the first one is fired before you enter the print dialog, so you can do whatever is necessary. The second one is triggered after the printing finishes, and you can use it for reverting to the original state.</p> <p>You can register your event listeners like this (of course, this may be different if using a framework):</p> <div class="gatsby-code-button-container" data-toaster-id="6662465538934881000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`window.onafterprint = (event) => { console.log('Printing finished!'); };`, `6662465538934881000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="javascript"><pre class="language-javascript"><code class="language-javascript">window<span class="token punctuation">.</span><span class="token function-variable function">onafterprint</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Printing finished!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre></div> <p>It is usually preferred to do as much as possible in CSS, but sometimes using JS may be necessary.</p> <h2 id="dev-tools-print-preview" style="position:relative;"><a href="#dev-tools-print-preview" aria-label="dev tools print preview permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dev Tools Print Preview</h2> <p>It can be time-consuming to open print preview dialog in your browser whenever you make changes and want to check how your page looks when printing. Fortunately, in Developer Tools, you can easily emulate how the page would look like when printing without triggering he print dialog.</p> <h3 id="chrome" style="position:relative;"><a href="#chrome" aria-label="chrome permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Chrome</h3> <p>In Chrome Devtools the you can <a href="https://developers.google.com/web/tools/chrome-devtools/css/print-preview">switch to print view</a>: </p> <ol> <li>Open your Devtools</li> <li>Press or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (or <kbd>⌘</kbd>+<kbd>⇧</kbd>+<kbd>P</kbd> on Mac)</li> <li>Search for <code class="language-text">rendering</code></li> <li>Select <code class="language-text">Show Rendering</code> and press <kbd>Enter</kbd></li> </ol> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 68%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABUElEQVQoz5VSTUvDQBTcn108eFAPCh4s9FBFRCoIfqFUbKt4C82td0sPijaHptkkm32bRFujs1mFiG6LcwiP3czOvHmPvclXKZKiKD7+D3bx1H0MPR7wyWTi+z79hFJKWiCEYN3D+kvPGT+PH4bD0WgURZG5S76R/kKWZWAOBgNWrF1ljY6SWgQXODUcFHEc4/BPw3meO47D5PYlHdxTScDfsFpVs3ULcr/fZ3L9hJpdKJMiKt3C1dKovshJrUU77VQTpSplZ7MZwn8vUZSwk1eOZP1aZ0qSyqjQJ56oBm63vXpMjRtKtGFDAx/N47vcttw4pWYPe4J4QaiOyhSLyIS09+/Qq6hMyGRma7iivHlOe7faNtSEMPpmZottu67LZK2l6u0UG5KlSo9LJ2SmjcKmPJ/PscuMb53x3U7EOQ/DYDoNggCl+eKJ3ALsj+d5n7HpFcJfXtmPAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/85992f75c1f846f94e68efee8474c0d3/c54d4/chrome-devtools-show-rendering.webp 175w, /static/85992f75c1f846f94e68efee8474c0d3/a3432/chrome-devtools-show-rendering.webp 350w, /static/85992f75c1f846f94e68efee8474c0d3/426ac/chrome-devtools-show-rendering.webp 700w, /static/85992f75c1f846f94e68efee8474c0d3/93852/chrome-devtools-show-rendering.webp 1010w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/85992f75c1f846f94e68efee8474c0d3/4edbd/chrome-devtools-show-rendering.png 175w, /static/85992f75c1f846f94e68efee8474c0d3/13ae7/chrome-devtools-show-rendering.png 350w, /static/85992f75c1f846f94e68efee8474c0d3/8c557/chrome-devtools-show-rendering.png 700w, /static/85992f75c1f846f94e68efee8474c0d3/5b587/chrome-devtools-show-rendering.png 1010w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/85992f75c1f846f94e68efee8474c0d3/8c557/chrome-devtools-show-rendering.png" alt="Chrome Devtool Show Rendering" title="Chrome Devtool Show Rendering" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>A new <code class="language-text">Rendering</code> window should appear.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 44.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAB2HAAAdhwGP5fFlAAABBElEQVQoz32R2XLCMAxF/f+/SAOEEOI63vfdFe0LU9LeOSPrQVeWbCSlpN/aGZ+X+7phKZUxRv8rKCCEoJQSEwLvZMGUCCd9iaXV1mrvbYwnkLyp926tRTlnL6XjHMhGA1Epx0U2dqQ0YhwNOhwIjKgrJaeJfkz8ciGnE5vOeadl34EMfJKK8aEZRkaNUuecSUmHoLw3MfqcQ62xVoi+lErInzcPytRyt5QWowMXHeZ8JYT22H7ZYGGIjDHUQ5DzzM5nebvx61UA8wyo5QbR3teh9aF52zYEh0/RBq+dV9YCEv7JOQDyXOv7wD9mjPHTLDh/rCsQYyw5xxAivPNL7eHO3vsvx20HkBtDKUgAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/a996d02aa51ccb18b10b936e3674e2ad/c54d4/rendering-emulate-print.webp 175w, /static/a996d02aa51ccb18b10b936e3674e2ad/a3432/rendering-emulate-print.webp 350w, /static/a996d02aa51ccb18b10b936e3674e2ad/426ac/rendering-emulate-print.webp 700w, /static/a996d02aa51ccb18b10b936e3674e2ad/c139f/rendering-emulate-print.webp 1050w, /static/a996d02aa51ccb18b10b936e3674e2ad/36c36/rendering-emulate-print.webp 1116w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/a996d02aa51ccb18b10b936e3674e2ad/4edbd/rendering-emulate-print.png 175w, /static/a996d02aa51ccb18b10b936e3674e2ad/13ae7/rendering-emulate-print.png 350w, /static/a996d02aa51ccb18b10b936e3674e2ad/8c557/rendering-emulate-print.png 700w, /static/a996d02aa51ccb18b10b936e3674e2ad/e996b/rendering-emulate-print.png 1050w, /static/a996d02aa51ccb18b10b936e3674e2ad/ea64c/rendering-emulate-print.png 1116w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/a996d02aa51ccb18b10b936e3674e2ad/8c557/rendering-emulate-print.png" alt="Emulate Print Rendering in Devtools" title="Emulate Print Rendering in Devtools" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Here you can select:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">Emulate CSS media type → print</code></pre></div> <p>Although not related to printing, you can emulate some more interesting rendering options in this tab, such as whether the user prefers a light or dark theme or reduced motion, you can show FPS and more.</p> <h3 id="firefox" style="position:relative;"><a href="#firefox" aria-label="firefox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Firefox</h3> <p>In Firefox, you need just to click the <code class="language-text">Toggle print media simulation for the page</code> button marked in red in the following image.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 24.571428571428573%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAA7klEQVQY0x2OS07EMBBEc/+LsJkVcAnYgEAskMgkYkhiO47T/rV/bQtDqVZd/aQ3LFwtKiptjAatNQAIsUt59GhtALQ8FFvY7XO88mOT+7ZuPYILzsUglQWusyePNWAFi4yJZVmddaVQIcq5RAzrNM/fy7ZxxhhnHDH0+/DwZi/PcPcElxd3/x4eP3D82Y21pdY/NFGlGmOEfylrbddx1neyr4MLme1qFYpLUAYxkfMobTlDJZ8bUisdJ+/9eULOqZeotFa714AejcOSU4qxt1XCEG/Smc22M1M3wJJS6q+Hmqb5lfPrun7t+5gJfgE28BkIHU64NgAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/41e7dac3bc9ec1e92d13cbb4e08e4631/c54d4/firefox-print-preview.webp 175w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/a3432/firefox-print-preview.webp 350w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/426ac/firefox-print-preview.webp 700w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/c139f/firefox-print-preview.webp 1050w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/7f403/firefox-print-preview.webp 1400w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/eece3/firefox-print-preview.webp 2007w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/41e7dac3bc9ec1e92d13cbb4e08e4631/4edbd/firefox-print-preview.png 175w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/13ae7/firefox-print-preview.png 350w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/8c557/firefox-print-preview.png 700w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/e996b/firefox-print-preview.png 1050w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/2cefc/firefox-print-preview.png 1400w, /static/41e7dac3bc9ec1e92d13cbb4e08e4631/cdc63/firefox-print-preview.png 2007w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/41e7dac3bc9ec1e92d13cbb4e08e4631/8c557/firefox-print-preview.png" alt="Emulater Print Rendering in Firefox" title="Emulater Print Rendering in Firefox" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>Printing support is often overlooked and not implemented at all. Many users still print web pages, and you should take care to design your pages to be printer-friendly. It is usually not that hard, but it can greatly enhance the user experience and usability of your site.</p> <p>Even if users are not printing your pages on paper, they can print them to PDF so that they can read them later offline on their tablet or ebook reader. For these cases, print stylesheets are also useful.</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Java Pattern Matching: InstanceOf (JEP 305)]]>https://www.vojtechruzicka.com/java-pattern-matching-instanceof/https://www.vojtechruzicka.com/java-pattern-matching-instanceof/Sun, 05 Apr 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/99c4b1eaff3626cf36f5b75840f49581/b17f8/pattern-matching.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 45.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABQACBP/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFY9LhXEhR//8QAGhAAAgIDAAAAAAAAAAAAAAAAAQISMQADEf/aAAgBAQABBQJwCvWgj7ItS3n/xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwFn/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAgIDAAAAAAAAAAAAAAAAABEBMQMgIf/aAAgBAQAGPwKmSrO49P/EABwQAAICAgMAAAAAAAAAAAAAAAERAEEQcTFRsf/aAAgBAQABPyExeiVToHxFD3sDD5yhP//aAAwDAQACAAMAAAAQ0O//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8QSYFl/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERYf/aAAgBAgEBPxBR3Cs//8QAHhAAAgICAgMAAAAAAAAAAAAAAREAMSFxQWGRobH/2gAIAQEAAT8QwjgiE5FQqBtaBZ+IDu/M7RMs0Zdt8g9An//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/99c4b1eaff3626cf36f5b75840f49581/c54d4/pattern-matching.webp 175w, /static/99c4b1eaff3626cf36f5b75840f49581/a3432/pattern-matching.webp 350w, /static/99c4b1eaff3626cf36f5b75840f49581/426ac/pattern-matching.webp 700w, /static/99c4b1eaff3626cf36f5b75840f49581/c139f/pattern-matching.webp 1050w, /static/99c4b1eaff3626cf36f5b75840f49581/7f403/pattern-matching.webp 1400w, /static/99c4b1eaff3626cf36f5b75840f49581/fad48/pattern-matching.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/99c4b1eaff3626cf36f5b75840f49581/e52aa/pattern-matching.jpg 175w, /static/99c4b1eaff3626cf36f5b75840f49581/70ebb/pattern-matching.jpg 350w, /static/99c4b1eaff3626cf36f5b75840f49581/29d31/pattern-matching.jpg 700w, /static/99c4b1eaff3626cf36f5b75840f49581/9ecec/pattern-matching.jpg 1050w, /static/99c4b1eaff3626cf36f5b75840f49581/d165a/pattern-matching.jpg 1400w, /static/99c4b1eaff3626cf36f5b75840f49581/b17f8/pattern-matching.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/99c4b1eaff3626cf36f5b75840f49581/29d31/pattern-matching.jpg" alt="Java Pattern Matching InstanceOf" title="Java Pattern Matching InstanceOf" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="instanceof" style="position:relative;"><a href="#instanceof" aria-label="instanceof permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>instanceof</h2> <p>To better understand this new feature, let's look into how <code class="language-text">instanceof</code> operator works. If you are already familiar with it, feel free to skip to the next section.</p> <p>In short, it tests whether the given object is of the given type. Based on this, it returns either <code class="language-text">true</code> or <code class="language-text">false</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="52426190411640560000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(` if(animal instanceof Cat) { // It is a cat! Do something } else { // It is not a cat, do something else }`, `52426190411640560000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"> <span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// It is a cat! Do something</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// It is not a cat, do something else</span> <span class="token punctuation">}</span></code></pre></div> <p>This returns <code class="language-text">true</code> if your object is of the given type or its subtype. Otherwise, it returns false.</p> <h2 id="pattern-matching-for-instanceof" style="position:relative;"><a href="#pattern-matching-for-instanceof" aria-label="pattern matching for instanceof permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pattern matching for instanceof</h2> <h3 id="the-good-old-way" style="position:relative;"><a href="#the-good-old-way" aria-label="the good old way permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The good old way</h3> <p>Consider the following example. <code class="language-text">Animal</code> is a class which has two subclasses: <code class="language-text">Cat</code> and <code class="language-text">Dog</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="89675361819995620000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public String getAnimalSound(Animal animal) { if(animal instanceof Cat) { Cat cat = (Cat)animal; return cat.meow(); } else if (animal instanceof Dog) { Dog dog = (Dog)animal; return dog.bark(); } throw new UnknownAnimalException(); }`, `89675361819995620000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getAnimalSound</span><span class="token punctuation">(</span><span class="token class-name">Animal</span> animal<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Cat</span> cat <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Cat</span><span class="token punctuation">)</span>animal<span class="token punctuation">;</span> <span class="token keyword">return</span> cat<span class="token punctuation">.</span><span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Dog</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Dog</span> dog <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Dog</span><span class="token punctuation">)</span>animal<span class="token punctuation">;</span> <span class="token keyword">return</span> dog<span class="token punctuation">.</span><span class="token function">bark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">UnknownAnimalException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>We get an animal as an input. If that animal is an instance of <code class="language-text">Cat</code>, we want to get that cat to meow. If it is a dog, we need it to bark. Since these methods are not on <code class="language-text">Animal</code> class, but rather on its subclasses, we need to:</p> <ol> <li>First, check what type of animal we have using <code class="language-text">instanceof</code>.</li> <li>Create a new variable of type <code class="language-text">Cat</code> or <code class="language-text">Dog</code>.</li> <li>Cast our animal to the proper type.</li> </ol> <p>Now we can use our cat or dog. Variation of this can be pretty common.</p> <p>The code does its job, but it is unnecessarily verbose. We usually want to do all the steps above. The casting is unnecessary as we already checked with <code class="language-text">instanceof</code> that we have either <code class="language-text">Cat</code> or <code class="language-text">Dog</code>.</p> <p>Fortunately, Java 14 introduced a new feature called <em>Pattern matching for instanceof</em> described in <a href="https://openjdk.java.net/jeps/305">JEP 305</a>. It is currently a preview feature, so it may change in a future release.</p> <h3 id="the-better-way" style="position:relative;"><a href="#the-better-way" aria-label="the better way permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The better way</h3> <p>In Java 14, the example above can be simplified.</p> <div class="gatsby-code-button-container" data-toaster-id="39427507024921150000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`// Before Java 14 if(animal instanceof Cat) { Cat cat = (Cat)animal; return cat.meow(); } //After if(animal instanceof Cat cat) { return cat.meow(); }`, `39427507024921150000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token comment">// Before Java 14</span> <span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Cat</span> cat <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Cat</span><span class="token punctuation">)</span>animal<span class="token punctuation">;</span> <span class="token keyword">return</span> cat<span class="token punctuation">.</span><span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//After</span> <span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span> cat<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> cat<span class="token punctuation">.</span><span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Here's what changed:</p> <ol> <li>No need to declare cat variable, it is available for us.</li> <li>There is no casting. We can use the cat as if it was of type <code class="language-text">Cat</code>.</li> <li>The scope of the<code class="language-text">cat</code> variable is only inside the <code class="language-text">if</code> block.</li> </ol> <p>This is simpler, more concise, easier to read, and less error-prone.</p> <h4 id="variable-scope" style="position:relative;"><a href="#variable-scope" aria-label="variable scope permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Variable scope</h4> <p>As already mentioned, the scope of the variable is only limited to the <code class="language-text">if</code> block:</p> <div class="gatsby-code-button-container" data-toaster-id="96633533030801490000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`if(animal instanceof Cat cat) { return cat.meow(); } else { // Can't use cat here } // Can't use cat here either`, `96633533030801490000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span> cat<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> cat<span class="token punctuation">.</span><span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Can't use cat here</span> <span class="token punctuation">}</span> <span class="token comment">// Can't use cat here either</span></code></pre></div> <p>However, you can use the variable inside the <code class="language-text">if</code> condition, if you have more complicated conditions, such as with AND/OR.</p> <div class="gatsby-code-button-container" data-toaster-id="77510573901168980000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`if(animal instanceof Cat cat && cat.isAlive()) { return cat.meow(); }`, `77510573901168980000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">if</span><span class="token punctuation">(</span>animal <span class="token keyword">instanceof</span> <span class="token class-name">Cat</span> cat <span class="token operator">&amp;&amp;</span> cat<span class="token punctuation">.</span><span class="token function">isAlive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> cat<span class="token punctuation">.</span><span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>After checking <code class="language-text">instanceof</code>, after <code class="language-text">&amp;&amp;</code>, we can use the <code class="language-text">cat</code> variable already typed as <code class="language-text">Cat</code>, not <code class="language-text">Animal</code>.</p> <h2 id="idea-support" style="position:relative;"><a href="#idea-support" aria-label="idea support permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>IDEA Support</h2> <p>The good news is that there is good support for this feature in IntelliJ IDEA, introduced in version <a href="https://blog.jetbrains.com/idea/2020/03/java-14-and-intellij-idea/">2020.1</a> (along with support for other Java 14 new features, such as <a href="https://www.vojtechruzicka.com/java-records/">Records</a> or <a href="https://www.vojtechruzicka.com/java-enhanced-switch/">Enhanced Switch</a>)</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/259dec57a884c665bc16aa851acbd3eb/5205c/idea-patter-matching-instanceof.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 35.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABXElEQVQY00VPTU/CQBDtD9dojAc9GT40ejEmJnhBLsaAitGE4MUIREBAPpZCabvQ7m5Lt11auuvamvjyZt5M8l4yowghgmDb7eoATIejsW7oc81ttmC7s2p3rP4ALxamOpsjsmbhNhZCVsxFCoWHyy2DgadbsD9XG2SmuQCFnkbMAQSfvj2hVm81azAyEgIlxIIjES0lFR4hSSFcbGsaGLQak9xl6azycXxb272uHhXrB4WXnavH/cJrvtLKlZuHpfebWltwwrdY4VzEyR2OE45HpNmz8ndv2fLwvAoyDyBbGZ8+gYvnaaYyzj2MTu6/94pfhbom/TKjSGEsoJSmu2yBT4lluPaSkhWCmrMyNpQwD6/x0nesmK2lLY7j5Gf+9346RFEEIdQNE0xVXTdUdWaY0A8CtgnDKJLchFHAWGr+D6eQYWTbGGOEEMaJ2r9wHcenHvU8uvb85EyJHxvhewL4xVowAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/259dec57a884c665bc16aa851acbd3eb/c54d4/idea-patter-matching-instanceof.webp 175w, /static/259dec57a884c665bc16aa851acbd3eb/a3432/idea-patter-matching-instanceof.webp 350w, /static/259dec57a884c665bc16aa851acbd3eb/426ac/idea-patter-matching-instanceof.webp 700w, /static/259dec57a884c665bc16aa851acbd3eb/184c4/idea-patter-matching-instanceof.webp 833w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/259dec57a884c665bc16aa851acbd3eb/4edbd/idea-patter-matching-instanceof.png 175w, /static/259dec57a884c665bc16aa851acbd3eb/13ae7/idea-patter-matching-instanceof.png 350w, /static/259dec57a884c665bc16aa851acbd3eb/8c557/idea-patter-matching-instanceof.png 700w, /static/259dec57a884c665bc16aa851acbd3eb/5205c/idea-patter-matching-instanceof.png 833w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/259dec57a884c665bc16aa851acbd3eb/8c557/idea-patter-matching-instanceof.png" alt="IDEA pattern matching instanceof" title="IDEA pattern matching instanceof" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="try-it-yourself" style="position:relative;"><a href="#try-it-yourself" aria-label="try it yourself permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Try it yourself!</h2> <p>To try this feature yourself, you'll need to have <a href="https://jdk.java.net/14/">JDK 14 installed</a>. </p> <h3 id="preview-feature" style="position:relative;"><a href="#preview-feature" aria-label="preview feature permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Preview feature</h3> <p>Pattern matching for instanceof functionality is available in Java 14. However, currently only as a preview feature. What does it mean?</p> <blockquote> <p>A preview language or VM feature is a new feature of the Java SE Platform that is fully specified, fully implemented, and yet impermanent. It is available in a JDK feature release to provoke developer feedback based on real-world use; this may lead to it becoming permanent in a future Java SE Platform.</p> <p>Before the next JDK feature release, the feature's "real world" strengths and weaknesses will be evaluated to decide if the feature has a long-term role in the Java SE Platform and, if so, whether it needs refinement. Consequently, the feature may be granted final and permanent status (with or without refinements), or undergo a further preview period (with or without refinements), or else be removed.</p> </blockquote> <p>Such features are shipped in the JDK but are not enabled by default. You need to explicitly enable them to use them. Needless to say, it is not intended for production use, but rather for evaluation and experimentation as it may get removed or heavily changed in a future release.</p> <h3 id="intellij-idea-setup" style="position:relative;"><a href="#intellij-idea-setup" aria-label="intellij idea setup permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>IntelliJ IDEA setup</h3> <p>In IntelliJ IDEA, you can enable preview features under <code class="language-text">File → Project Structure</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/a8fd7539a988c12c8f08f33909bcb8f1/0b79a/idea-preview-settings.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 41.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABOElEQVQY032QTU/DMAyG+4+5IvglnIAjIHFHXDgwEKyFdWvTbu2mpGnSLEnTpl9c8dpJSEjwHBzLeh2/trPwEGHFDmdcqKq2esRa2zT2H5oRB6RSylLroe+NKaM4iuKYcT4MX8PftG17aD69nJ1dPZ9cPJ3fBA8fWaX3OWOVMX3f/2roR6bkOPlutrt+XN2/Zbev9GWFP913d+4mSbJNt8Bms8EYg7X9AamUgtQYc2yWUoHSNq1tO9iF7RUhWZ7noOW8AMqyrGsLbZBTmgshoHK0jVLi+ijBjIqKCwkLB0GIECKEwF8g6roOdBDBLcTJ9lR0vCWaL1aLYI25YkIufd/zvKUPrx+G4TpehyM0o2yEUgrnBGt1XTuEySglKeFUGF3Zcjy+1gpsi6KAXIgChsBAUJvqByh+A6txsA16bLTUAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/a8fd7539a988c12c8f08f33909bcb8f1/c54d4/idea-preview-settings.webp 175w, /static/a8fd7539a988c12c8f08f33909bcb8f1/a3432/idea-preview-settings.webp 350w, /static/a8fd7539a988c12c8f08f33909bcb8f1/426ac/idea-preview-settings.webp 700w, /static/a8fd7539a988c12c8f08f33909bcb8f1/c139f/idea-preview-settings.webp 1050w, /static/a8fd7539a988c12c8f08f33909bcb8f1/d6e92/idea-preview-settings.webp 1331w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/a8fd7539a988c12c8f08f33909bcb8f1/4edbd/idea-preview-settings.png 175w, /static/a8fd7539a988c12c8f08f33909bcb8f1/13ae7/idea-preview-settings.png 350w, /static/a8fd7539a988c12c8f08f33909bcb8f1/8c557/idea-preview-settings.png 700w, /static/a8fd7539a988c12c8f08f33909bcb8f1/e996b/idea-preview-settings.png 1050w, /static/a8fd7539a988c12c8f08f33909bcb8f1/0b79a/idea-preview-settings.png 1331w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/a8fd7539a988c12c8f08f33909bcb8f1/8c557/idea-preview-settings.png" alt="Idea Preview features" title="Idea Preview features" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h3 id="manual-compilation" style="position:relative;"><a href="#manual-compilation" aria-label="manual compilation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Manual compilation</h3> <p>Alternatively, if building manually, you need to provide the following params to <code class="language-text">javac</code>:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">javac --release 14 --enable-preview ...</code></pre></div> <p>That is for compile-time. At run-time, you just provide <code class="language-text">--enable-preview</code></p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">java --enable-preview ...</code></pre></div> <h3 id="maven-projects" style="position:relative;"><a href="#maven-projects" aria-label="maven projects permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Maven projects</h3> <p>For Maven builds, you can use the following configuration:</p> <div class="gatsby-code-button-container" data-toaster-id="59430263000527960000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <release>14</release> <compilerArgs> --enable-preview </compilerArgs> <source>14</source> <target>14</target> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>--enable-preview</argLine> </configuration> </plugin> <plugin> <artifactId>maven-failsafe-plugin</artifactId> <configuration> <argLine>--enable-preview</argLine> </configuration> </plugin> </plugins> </build>`, `59430263000527960000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>build</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-compiler-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>release</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>release</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>compilerArgs</span><span class="token punctuation">></span></span> --enable-preview <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>compilerArgs</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>source</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>target</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>target</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-surefire-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>argLine</span><span class="token punctuation">></span></span>--enable-preview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>argLine</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-failsafe-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>argLine</span><span class="token punctuation">></span></span>--enable-preview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>argLine</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>build</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="update-java-15" style="position:relative;"><a href="#update-java-15" aria-label="update java 15 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>UPDATE: Java 15</h2> <p>In Java 15, this feature is still present as a preview feture - <a href="https://openjdk.java.net/jeps/375">JEP 375: Pattern Matching for instanceof (Second Preview)</a>. Although there is a new JEP for it, <a href="https://bugs.openjdk.java.net/browse/JDK-8235186">there are no changes</a> relative to the Java 14 version.</p> <h2 id="also-new-in-java-14" style="position:relative;"><a href="#also-new-in-java-14" aria-label="also new in java 14 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Also new in Java 14</h2> <ul> <li><a href="https://www.vojtechruzicka.com/java-records/">Records</a></li> <li><a href="https://www.vojtechruzicka.com/java-enhanced-switch/">Enhanced Switch</a></li> <li><a href="https://www.vojtechruzicka.com/java-text-blocks/">Text Blocks</a></li> </ul>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[How to share run configurations in IntelliJ IDEA]]>https://www.vojtechruzicka.com/idea-sharing-run-configurations/https://www.vojtechruzicka.com/idea-sharing-run-configurations/Fri, 03 Apr 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAME/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAZ30Qm9AH//EABoQAAIDAQEAAAAAAAAAAAAAAAECAAMSERP/2gAIAQEAAQUC8KyAFSL0qy5A1ZCk/8QAFREBAQAAAAAAAAAAAAAAAAAAACH/2gAIAQMBAT8BR//EABURAQEAAAAAAAAAAAAAAAAAAAAh/9oACAECAQE/Aar/xAAaEAADAQADAAAAAAAAAAAAAAAAASERAjJh/9oACAEBAAY/ApxZFhpCvF4dj//EABwQAQADAAIDAAAAAAAAAAAAAAEAETEhQVFhsf/aAAgBAQABPyHRK9jk40SerVlF9NQwzUfB2lDYnlP/2gAMAwEAAgADAAAAEO/P/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARUf/aAAgBAwEBPxARt6v/xAAYEQACAwAAAAAAAAAAAAAAAAAAYQERIf/aAAgBAgEBPxCWwWj/xAAcEAEAAwACAwAAAAAAAAAAAAABABEhMXFBYbH/2gAIAQEAAT8QdEPKJ2t5+wqmLimfcYoqzd19TPo2CWRMCKKry8svlAVxU//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/1a17f6d82072047e97a02a661b95cbff/c54d4/idea-sharing-run-configurations.webp 175w, /static/1a17f6d82072047e97a02a661b95cbff/a3432/idea-sharing-run-configurations.webp 350w, /static/1a17f6d82072047e97a02a661b95cbff/426ac/idea-sharing-run-configurations.webp 700w, /static/1a17f6d82072047e97a02a661b95cbff/c139f/idea-sharing-run-configurations.webp 1050w, /static/1a17f6d82072047e97a02a661b95cbff/7f403/idea-sharing-run-configurations.webp 1400w, /static/1a17f6d82072047e97a02a661b95cbff/fad48/idea-sharing-run-configurations.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/1a17f6d82072047e97a02a661b95cbff/e52aa/idea-sharing-run-configurations.jpg 175w, /static/1a17f6d82072047e97a02a661b95cbff/70ebb/idea-sharing-run-configurations.jpg 350w, /static/1a17f6d82072047e97a02a661b95cbff/29d31/idea-sharing-run-configurations.jpg 700w, /static/1a17f6d82072047e97a02a661b95cbff/9ecec/idea-sharing-run-configurations.jpg 1050w, /static/1a17f6d82072047e97a02a661b95cbff/d165a/idea-sharing-run-configurations.jpg 1400w, /static/1a17f6d82072047e97a02a661b95cbff/b17f8/idea-sharing-run-configurations.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/1a17f6d82072047e97a02a661b95cbff/29d31/idea-sharing-run-configurations.jpg" alt="IDEA Sharing Run Configurations" title="IDEA Sharing Run Configurations" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Finally, with IDEA 2020.1, you can easily share your run configurations among your team members utilizing version control.</p> <h2 id="idea-settings-sharing" style="position:relative;"><a href="#idea-settings-sharing" aria-label="idea settings sharing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>IDEA settings sharing</h2> <p>When developing in a team, it is vital to stick to a common set of practices to keep consistency in the development process. Having your IDE configured in the similar way can greatly help you with this. Fortunately, IDEA offers several ways of settings sharing, which can be very useful in such situations. It can be also beneficial for individuals when working with multiple machines. You can read more about it in the following article:</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/intellij-idea-tips-tricks-synchronization-sharing-settings/" style="box-shadow: none;">IntelliJ IDEA Tips &amp; Tricks: Synchronization and Sharing of Settings</a></h4><small class="front-post-info"><span class="front-post-info-date">24 January, 2018</span><div class="post-tags"><ul><li><a href="/tags/idea/">#IDEA</a></li></ul></div></small><div><a class="front-post-image" href="/intellij-idea-tips-tricks-synchronization-sharing-settings/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 66.6667%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAB/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAP/2gAMAwEAAhADEAAAAV2pjckiP//EABgQAAMBAQAAAAAAAAAAAAAAAAABEQJB/9oACAEBAAEFAkoPSRUZwjlh/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8BZ//EABURAQEAAAAAAAAAAAAAAAAAABAR/9oACAECAQE/AYf/xAAbEAABBAMAAAAAAAAAAAAAAAAAAhAREiEygf/aAAgBAQAGPwIwm3DQl//EABwQAQACAgMBAAAAAAAAAAAAAAEAETFxECGBkf/aAAgBAQABPyEDaAahlrpFlZ+RJ0frwcBP/9oADAMBAAIAAwAAABDw7//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EGP/xAAXEQEBAQEAAAAAAAAAAAAAAAABABFR/9oACAECAQE/EAcsL//EABsQAQEBAAMBAQAAAAAAAAAAAAERACFBgTGR/9oACAEBAAE/EJ1b6oJjhzp4P14yRQk5gZ7MWdDqx6XDIS9ZFCG//9k=" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition-delay: 500ms;"><picture><source srcset="/linked/idea-settings/5e4a3/idea-settings-featured.jpg 45w, /linked/idea-settings/e451c/idea-settings-featured.jpg 90w, /linked/idea-settings/29fd0/idea-settings-featured.jpg 180w, /linked/idea-settings/b3ebb/idea-settings-featured.jpg 270w, /linked/idea-settings/8841e/idea-settings-featured.jpg 360w, /linked/idea-settings/2b1a3/idea-settings-featured.jpg 900w" sizes="(max-width: 180px) 100vw, 180px"><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/idea-settings/5e4a3/idea-settings-featured.jpg 45w, /linked/idea-settings/e451c/idea-settings-featured.jpg 90w, /linked/idea-settings/29fd0/idea-settings-featured.jpg 180w, /linked/idea-settings/b3ebb/idea-settings-featured.jpg 270w, /linked/idea-settings/8841e/idea-settings-featured.jpg 360w, /linked/idea-settings/2b1a3/idea-settings-featured.jpg 900w" src="/linked/idea-settings/29fd0/idea-settings-featured.jpg" alt="" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 500ms ease 0s;"></picture><noscript><picture><source srcset="/linked/idea-settings/5e4a3/idea-settings-featured.jpg 45w, /linked/idea-settings/e451c/idea-settings-featured.jpg 90w, /linked/idea-settings/29fd0/idea-settings-featured.jpg 180w, /linked/idea-settings/b3ebb/idea-settings-featured.jpg 270w, /linked/idea-settings/8841e/idea-settings-featured.jpg 360w, /linked/idea-settings/2b1a3/idea-settings-featured.jpg 900w" sizes="(max-width: 180px) 100vw, 180px" /><img loading="lazy" sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/idea-settings/5e4a3/idea-settings-featured.jpg 45w, /linked/idea-settings/e451c/idea-settings-featured.jpg 90w, /linked/idea-settings/29fd0/idea-settings-featured.jpg 180w, /linked/idea-settings/b3ebb/idea-settings-featured.jpg 270w, /linked/idea-settings/8841e/idea-settings-featured.jpg 360w, /linked/idea-settings/2b1a3/idea-settings-featured.jpg 900w" src="/linked/idea-settings/29fd0/idea-settings-featured.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">Did you know IDEA offers two ways of synchronization of you settings among multiple instances of your IDE? You can use it when working on multiple computers or to share unified settings in your team.</span></div></div> <h2 id="run-configuration-sharing" style="position:relative;"><a href="#run-configuration-sharing" aria-label="run configuration sharing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Run configuration sharing</h2> <p>You can see your Run Configurations of top of the IDE window next to <code class="language-text">Run</code> and <code class="language-text">Debug</code> buttons.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 10.285714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAZklEQVQI102KWw6CMBAAuf/ZQGpSFShsyr66pZJYT2DDl5PJfE0XY9yQdiJmIiQVNbOUUs659bhQVXz4MI6ru6/ONdF7QOgEgmAU3NvBzKWU7x+11qufvAV8Ped+WIabTPMJYG/7AVliaou2ux+3AAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/418c14eef5784e5b1eea5f2dbe4100bb/c54d4/run-consfigurations.webp 175w, /static/418c14eef5784e5b1eea5f2dbe4100bb/a3432/run-consfigurations.webp 350w, /static/418c14eef5784e5b1eea5f2dbe4100bb/426ac/run-consfigurations.webp 700w, /static/418c14eef5784e5b1eea5f2dbe4100bb/c139f/run-consfigurations.webp 1050w, /static/418c14eef5784e5b1eea5f2dbe4100bb/7f403/run-consfigurations.webp 1400w, /static/418c14eef5784e5b1eea5f2dbe4100bb/15ee3/run-consfigurations.webp 1489w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/418c14eef5784e5b1eea5f2dbe4100bb/4edbd/run-consfigurations.png 175w, /static/418c14eef5784e5b1eea5f2dbe4100bb/13ae7/run-consfigurations.png 350w, /static/418c14eef5784e5b1eea5f2dbe4100bb/8c557/run-consfigurations.png 700w, /static/418c14eef5784e5b1eea5f2dbe4100bb/e996b/run-consfigurations.png 1050w, /static/418c14eef5784e5b1eea5f2dbe4100bb/2cefc/run-consfigurations.png 1400w, /static/418c14eef5784e5b1eea5f2dbe4100bb/f42d7/run-consfigurations.png 1489w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/418c14eef5784e5b1eea5f2dbe4100bb/8c557/run-consfigurations.png" alt="IDEA Run Configurations location" title="IDEA Run Configurations location" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>A configuration can be as simple as running your project. You can have one for running tests, deploying to an application server, or various others. Configurations can get more complicated as you introduce more settings such as environmental variables, command-line arguments, VM options, and much more.</p> <p>In the case of these more complicated configs, it is vital to be able to share them with your team, so they can use the app right after cloning with no advanced setup. Not only is it useful for simplification of the initial setup, but it can also help to ensure that everybody is running with the same settings.</p> <p>To edit individual configurations, you can hit double <kbd>Shift</kbd> and then search for <code class="language-text">Edit Configurations...</code>.</p> <h3 id="default-behavior" style="position:relative;"><a href="#default-behavior" aria-label="default behavior permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Default behavior</h3> <p>The default behavior of IDEA run configurations is not very sharing-friendly. All your run configs are shared in <code class="language-text">.idea/workspace.xml</code>.</p> <p>This is problematic as the file also contains a lot of user-specific information, so it is not really suitable for sharing among team members. As a result the whole <code class="language-text">.idea</code> folder usually ends up in <code class="language-text">.gitignore</code> file.</p> <h3 id="share-through-vcs" style="position:relative;"><a href="#share-through-vcs" aria-label="share through vcs permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Share through VCS</h3> <p>As a result, IDEA later introduced the concept of sharing your run configuration through VCS.</p> <p>This is configured per each configuration so that you can have both your personal and shared configurations at the same time.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 31.428571428571427%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAB2GAAAdhgFdohOBAAAA/0lEQVQY04VRa2+EIBD0///BO8+rD3yAioIPfPXidJfW5j40KclkCOzOzkCQRCkqWSHPcyilMAwDrLXEFsYYv5+myWMcR4zEVtXQRYm+qmDyAn1dw1AdawS617jdb3g+n15Aa400E0hSAUEFcRyj6zov5pyDWxxsWUE+IsID7T1ELXJIMpNlGYK2bRGGIYQQ3k3X94iSHB8ZTbcD+L4sS0gpf6A8KzozNMiRYx5kL4evzxfe13EcFJ0aqIlj/z6B/eYLHHEg1/4pptHvi6JAwAK8zvP0vK6rj89R2R1P3/cd27Z5fgefXXDL4pP8KciT2H7TNJjnGQsV/wf+LO77AiAMxqfBzzOHAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e1b5ed7128f73efbeffd3f5f6630b18c/c54d4/idea-share-through-vcs.webp 175w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/a3432/idea-share-through-vcs.webp 350w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/426ac/idea-share-through-vcs.webp 700w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/c139f/idea-share-through-vcs.webp 1050w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/7f403/idea-share-through-vcs.webp 1400w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/0ddd7/idea-share-through-vcs.webp 1790w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/e1b5ed7128f73efbeffd3f5f6630b18c/4edbd/idea-share-through-vcs.png 175w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/13ae7/idea-share-through-vcs.png 350w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/8c557/idea-share-through-vcs.png 700w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/e996b/idea-share-through-vcs.png 1050w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/2cefc/idea-share-through-vcs.png 1400w, /static/e1b5ed7128f73efbeffd3f5f6630b18c/07d37/idea-share-through-vcs.png 1790w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/e1b5ed7128f73efbeffd3f5f6630b18c/8c557/idea-share-through-vcs.png" alt="IDEA share run config through VCS" title="IDEA share run config through VCS" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>It works in a way that it stores your shared run configurations in <code class="language-text">.idea/runConfigurations</code>. Each configuration has its own separate file. This is better, but still not ideal. Ignoring the whole <code class="language-text">.idea</code> dir does not work anymore, and you need to have more complicated config to ignore everything except the run configs directory. Otherwise, it won't work.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 22.285714285714285%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAq0lEQVQI10WO6w6CMAyFef+Hw4SbBMXZjW3dzW0E5KdW0PilPWmanvQUwEbBOeNACgDGmJRS/BOcU3d+7dqqa+v+3EgJTI6IU3q4wmlN5RHpcJ7nnHP6EqmNFnVdDt1pujWCtWyoxr4kBbh4wwvvvSWMkVIi4rIs67o+d1L0qCfCW+6t0krICUhzDpQoxVAotP0V+MeISinnXAjh+H/kpnkPNNNGa9y27fXjDREp2m1ecxIdAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/4f6eee64612efcf6d1e30d5e407bd962/c54d4/idea-share-through-vcs-ignored.webp 175w, /static/4f6eee64612efcf6d1e30d5e407bd962/a3432/idea-share-through-vcs-ignored.webp 350w, /static/4f6eee64612efcf6d1e30d5e407bd962/426ac/idea-share-through-vcs-ignored.webp 700w, /static/4f6eee64612efcf6d1e30d5e407bd962/c139f/idea-share-through-vcs-ignored.webp 1050w, /static/4f6eee64612efcf6d1e30d5e407bd962/7f403/idea-share-through-vcs-ignored.webp 1400w, /static/4f6eee64612efcf6d1e30d5e407bd962/bb2de/idea-share-through-vcs-ignored.webp 1733w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/4f6eee64612efcf6d1e30d5e407bd962/4edbd/idea-share-through-vcs-ignored.png 175w, /static/4f6eee64612efcf6d1e30d5e407bd962/13ae7/idea-share-through-vcs-ignored.png 350w, /static/4f6eee64612efcf6d1e30d5e407bd962/8c557/idea-share-through-vcs-ignored.png 700w, /static/4f6eee64612efcf6d1e30d5e407bd962/e996b/idea-share-through-vcs-ignored.png 1050w, /static/4f6eee64612efcf6d1e30d5e407bd962/2cefc/idea-share-through-vcs-ignored.png 1400w, /static/4f6eee64612efcf6d1e30d5e407bd962/a303f/idea-share-through-vcs-ignored.png 1733w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/4f6eee64612efcf6d1e30d5e407bd962/8c557/idea-share-through-vcs-ignored.png" alt="IDEA share run config through VCS ignored folder" title="IDEA share run config through VCS ignored folder" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h3 id="store-project-file" style="position:relative;"><a href="#store-project-file" aria-label="store project file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Store project file</h3> <p>In version 2020.1, you can now <a href="https://blog.jetbrains.com/idea/2020/03/intellij-idea-2020-1-beta2/">easily store run configurations</a> as external files, not necessarily under <code class="language-text">.idea</code> directory.</p> <p>This replaces the old <code class="language-text">Share through VCS</code> option. For backward compatibility with older IDE versions, you can still store your files under <code class="language-text">.idea/runConfigurations</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/UlEQVQY001Q227DIAzl/7+v06ZqSwgYY242oZ3Wvc0kq9QjhMA6cC5mP9B7Pw9jjNsLxoF+QEmyjydzEKER4ZwzEUXElDIz/7xA3yubInnvlWFJrEcMEBB1Yi5Xu7hwIkaqtXCrrVZuTZWGSkxfohpdWCZYmHVWSzGLdbXpZVfNnJKF+P65XT6+3q7r6pOn5rBkvvXvX7k/+v1x7jpxsZg4gf/JRXIVoOow+7mKri1QSA1LhzJ8Hi7vljq1W0jFbNu2rKvKnk0wNzWm38yKplXpT8PM0hqfzGle2IAHazdN32bI7j0gRk0JmQFAXcFsA3ITp51oVwBHcgkY/gCcUoJtSJ7EuQAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e28d5dc27a8e27dee716501ef3bc75b8/c54d4/idea-save-run-config-as-project-file.webp 175w, /static/e28d5dc27a8e27dee716501ef3bc75b8/a3432/idea-save-run-config-as-project-file.webp 350w, /static/e28d5dc27a8e27dee716501ef3bc75b8/426ac/idea-save-run-config-as-project-file.webp 700w, /static/e28d5dc27a8e27dee716501ef3bc75b8/c139f/idea-save-run-config-as-project-file.webp 1050w, /static/e28d5dc27a8e27dee716501ef3bc75b8/7f403/idea-save-run-config-as-project-file.webp 1400w, /static/e28d5dc27a8e27dee716501ef3bc75b8/0bf85/idea-save-run-config-as-project-file.webp 1497w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/e28d5dc27a8e27dee716501ef3bc75b8/4edbd/idea-save-run-config-as-project-file.png 175w, /static/e28d5dc27a8e27dee716501ef3bc75b8/13ae7/idea-save-run-config-as-project-file.png 350w, /static/e28d5dc27a8e27dee716501ef3bc75b8/8c557/idea-save-run-config-as-project-file.png 700w, /static/e28d5dc27a8e27dee716501ef3bc75b8/e996b/idea-save-run-config-as-project-file.png 1050w, /static/e28d5dc27a8e27dee716501ef3bc75b8/2cefc/idea-save-run-config-as-project-file.png 1400w, /static/e28d5dc27a8e27dee716501ef3bc75b8/a80b8/idea-save-run-config-as-project-file.png 1497w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/e28d5dc27a8e27dee716501ef3bc75b8/8c557/idea-save-run-config-as-project-file.png" alt="IDEA Save Run configuration as project file" title="IDEA Save Run configuration as project file" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>You can check <a href="/64c0fcb21d981cae72421968f026fd42/Config.run.xml">an example of stored run configuration file</a>.</p> <h2 id="importing-configuration" style="position:relative;"><a href="#importing-configuration" aria-label="importing configuration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Importing configuration</h2> <p>When creating a new project, IDEA can usually pick up your configurations easily when stored inside <code class="language-text">.idea/runConfigurations</code>. When using a different location, it can have trouble. You can still add these configurations manually, fortunately.</p> <p>Just go to <code class="language-text">Add configuration...</code> (Or <code class="language-text">Edit Configurations...</code>) and you should see your stored configs in the list. From there, you can easily add them as usual.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 61.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAB2GAAAdhgFdohOBAAAB50lEQVQoz21Sya7TQBD0//8CNw7c4MIBISGEQBxQDo9F4hBBsGOPPWN7vCZOnK3o6mSeIngjtWbp6urumo6SPwkK55AXBYxY7T28WF3Xuocz3wuTwSwWMA9fUX37jtJalKVDVVVqhcRHNlnBpjFclmCoHbq6RC3OpmngJFFZlvC1x3a7xTRNGIcBma1hjFF/ar0Wk+e5vkUf3i3w7PkbvHz1GW/f/8SXTz8QxymS7AoqbmAr1TDJIIR108LLuW1bNG0v1qLrOsVEa1Pid+qQOQGNB/huixUJUyMErbZCwqsVEuTgJJCJmICVbzYbjOOomOgw73G/zucjGk89cvR9r8DNOEhlo96Hodcqg423O3GqITNcic66z4cjfiUGy+USWZrClhVWqYWTBF7aWxcVbJHfZBANnUcm2tlb1Y+El8tF94MQFrbEer1WQkOxc4tOdGKVTderXrRGNey0dd615f8JD+jFScI4jnVkCgG2EkjfvN9jnmfsZeenEBva50Q8SchMWZZpZhJSO+KuNj2e+RnBeCc+4mzdLxJSCxKyghBMnM4hP4kkJLzz8Z3JIx5Op5MSHY9HbWW32+mdVQcLvo3g2XJYoTOScsQi9s1KgoVR+bclvoXxmKYtXi9SvPgY62fwnbEk/Auy8I0BxFBsPAAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/54b567fca7a46e0f15b6c4e6ace76c48/c54d4/idea-import-configuration.webp 175w, /static/54b567fca7a46e0f15b6c4e6ace76c48/a3432/idea-import-configuration.webp 350w, /static/54b567fca7a46e0f15b6c4e6ace76c48/426ac/idea-import-configuration.webp 700w, /static/54b567fca7a46e0f15b6c4e6ace76c48/c139f/idea-import-configuration.webp 1050w, /static/54b567fca7a46e0f15b6c4e6ace76c48/7f403/idea-import-configuration.webp 1400w, /static/54b567fca7a46e0f15b6c4e6ace76c48/53d81/idea-import-configuration.webp 1806w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/54b567fca7a46e0f15b6c4e6ace76c48/4edbd/idea-import-configuration.png 175w, /static/54b567fca7a46e0f15b6c4e6ace76c48/13ae7/idea-import-configuration.png 350w, /static/54b567fca7a46e0f15b6c4e6ace76c48/8c557/idea-import-configuration.png 700w, /static/54b567fca7a46e0f15b6c4e6ace76c48/e996b/idea-import-configuration.png 1050w, /static/54b567fca7a46e0f15b6c4e6ace76c48/2cefc/idea-import-configuration.png 1400w, /static/54b567fca7a46e0f15b6c4e6ace76c48/565cc/idea-import-configuration.png 1806w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/54b567fca7a46e0f15b6c4e6ace76c48/8c557/idea-import-configuration.png" alt="Manually adding IDEA run configurations" title="Manually adding IDEA run configurations" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>Sharing run configurations can greatly simplify your project setup and ensure consistency among your team members. For even better results, consider combining it with general IDEA settings sharing.</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[CSS Position guide]]>https://www.vojtechruzicka.com/css-position/https://www.vojtechruzicka.com/css-position/Tue, 31 Mar 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/b17f8/css-position.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABcBAAMBAAAAAAAAAAAAAAAAAAABAgP/2gAMAwEAAhADEAAAAbc62aNVjgf/xAAaEAEAAwADAAAAAAAAAAAAAAABAAISESEx/9oACAEBAAEFArLNhDaDyXe6+f/EABoRAAICAwAAAAAAAAAAAAAAAAABAgMSFFH/2gAIAQMBAT8Bisk62atfD//EABgRAAMBAQAAAAAAAAAAAAAAAAABAxEC/9oACAECAQE/AZZo79Jn/8QAFxABAQEBAAAAAAAAAAAAAAAAAQARIP/aAAgBAQAGPwKbQOP/xAAaEAADAAMBAAAAAAAAAAAAAAAAASERMYGx/9oACAEBAAE/Iamuj8CDKYJlVmpjQ0n/2gAMAwEAAgADAAAAEHjP/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAFRgf/aAAgBAwEBPxB2gZUf/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQARIUH/2gAIAQIBAT8QKt0jwKyf/8QAGBABAQEBAQAAAAAAAAAAAAAAAREhAEH/2gAIAQEAAT8QKBGiB8JzYSwxrekh2bOSUWvPcIszqeed/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/c54d4/css-position.webp 175w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/a3432/css-position.webp 350w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/426ac/css-position.webp 700w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/c139f/css-position.webp 1050w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/7f403/css-position.webp 1400w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/fad48/css-position.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/e52aa/css-position.jpg 175w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/70ebb/css-position.jpg 350w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/29d31/css-position.jpg 700w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/9ecec/css-position.jpg 1050w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/d165a/css-position.jpg 1400w, /static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/b17f8/css-position.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/3d0bdb5b1d7ba24fdbd55edaf9bfc989/29d31/css-position.jpg" alt="CSS Position" title="CSS Position" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="position-property" style="position:relative;"><a href="#position-property" aria-label="position property permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Position property</h2> <p>Positioning elements with CSS can be tricky. There are various ways to achieve the desired placement. One of them is the <code class="language-text">position</code> property.</p> <p>Based on its value, the position of an element can be calculated in different ways, such as relative to its normal position, relative to its parent or to the whole page.</p> <p>The position property can have any of these values:</p> <ul> <li>static</li> <li>absolute</li> <li>relative</li> <li>fixed</li> <li>sticky</li> </ul> <p>We'll cover the details of these values a bit later. As with other properties, you can specify <code class="language-text">inherit</code>, which means the value is determined from the parent element. This can be useful as, by default, <code class="language-text">position</code> does not cascade to its child elements.</p> <h2 id="placement-properties" style="position:relative;"><a href="#placement-properties" aria-label="placement properties permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Placement properties</h2> <p>Position property on its own is not that useful. It only says how the position should be calculated. For example, relative to the normal position of an element.</p> <p>But we also need to define <em>where</em> exactly the element should be placed, not only how. There are several properties we can use for that.</p> <ul> <li>top</li> <li>left</li> <li>right</li> <li>bottom</li> </ul> <p>These define how much the element's position should be adjusted and in which direction.</p> <h2 id="position-values" style="position:relative;"><a href="#position-values" aria-label="position values permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Position values</h2> <p>Now let's dig deeper into how different <code class="language-text">position</code> values work.</p> <h3 id="static" style="position:relative;"><a href="#static" aria-label="static permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>static</h3> <p>This is the default value, which is used if you don't specify anything else.</p> <p>Even though it is a default value, it can sometimes be useful to set it explicitly. For example, to override different <code class="language-text">position</code> value, which is set elsewhere.</p> <p>Unlike with other <code class="language-text">position</code> values, when using <code class="language-text">static</code>, properties such as <code class="language-text">top</code>, <code class="language-text">left</code>, <code class="language-text">bottom</code>, <code class="language-text">right</code>, or <code class="language-text">z-index</code> have no effect.</p> <h3 id="relative" style="position:relative;"><a href="#relative" aria-label="relative permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>relative</h3> <p>Relative position means that the element is placed <strong>relative to its original position in the page</strong>. When you set just <code class="language-text">position: relative;</code>, nothing will change. You also need to define how should the placement of the element change relative to its original position. This can be achieved using <code class="language-text">top</code>, <code class="language-text">bottom</code>, <code class="language-text">left</code> and <code class="language-text">right</code> properties.</p> <p>For example, <code class="language-text">top: 20px;</code> means that the element will be moved <strong>down</strong> 20 pixels from its original position. In other words, it will be placed 20 pixels from the top edge of its original position.</p> <p>In the example below, gray squares mark the original position of each element before it was moved using <code class="language-text">position: relative;</code>.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/mdJamVb/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>As you can see, the <strong>space required for the original location of the element is still allocated</strong>.</p> <p>As a result of an element being moved from its original position, there can be a situation, where multiple elements overlap each other. Fortunately, with <code class="language-text">z-index</code> property, you can control which elements should be in the front and which in the back. We'll discuss this in more detail later.</p> <h3 id="absolute" style="position:relative;"><a href="#absolute" aria-label="absolute permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>absolute</h3> <p>Absolute positioning is trickier. Unlike with the <code class="language-text">relative</code> position, elements are removed from the normal document flow, and <strong>their original space is not reserved</strong>.</p> <p>Using <code class="language-text">top</code>, <code class="language-text">bottom</code>, <code class="language-text">left</code>, and <code class="language-text">right</code> properties, you determine where the element should be placed <strong>relative to the first parent</strong> in the element hierarchy, which has <code class="language-text">position</code> set to anything else than <code class="language-text">static</code>.</p> <p><strong>If there is no such parent, it is set relative to the whole page.</strong></p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/vYObeJw/?height=400&amp;&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Each orange box is positioned relative to the gray container box. This is possible because the container has <code class="language-text">position</code> set to <code class="language-text">relative</code>. If the container's position was <code class="language-text">static</code>, the orange boxes <strong>would be positioned relative to the whole page</strong>.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/YzXBrLo/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Another important behavior to notice is that we explicitly set the height of the gray container to <code class="language-text">200 px</code>. That's because, unlike with <code class="language-text">relative</code> position, <strong>absolutely positioned elements are removed from the normal document flow and don't occupy any space when calculating the size of the parent container</strong>.</p> <p>When we don't specify the height of the gray container box, it will collapse to zero as it does not contain any elements, which are used to determine its size.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/JjdxrBw/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h3 id="fixed" style="position:relative;"><a href="#fixed" aria-label="fixed permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>fixed</h3> <p>Fixed positioning is similar to <code class="language-text">absolute</code> in a way that the element is also removed from the normal document flow, and its original space is not reserved.</p> <p>The position is determined relative to the viewport. This means that even if you scroll, the element preserves its original location on the page. This can be useful, for example, if you want to have a navigation bar, which is always visible on the top, no matter where you are on the page. Or a cookie info bar on the bottom.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/xxGMweV/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>Although this can be tempting, be careful with its usage. On a mobile device with a small screen, it can be a big deal if a large portion of your screen is always covered with a navigation bar or something similar. It dramatically reduces space for the actual content of your page and can significantly limit usability.</p> <h3 id="sticky" style="position:relative;"><a href="#sticky" aria-label="sticky permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>sticky</h3> <p>The sticky position is the latest addition to the list of <code class="language-text">position</code> properties. It is a hybrid between <code class="language-text">relative</code> and <code class="language-text">fixed</code> positions.</p> <p>The element is normally positioned on the page, but when you reach its position when scrolling, its position becomes fixed. It sticks to its location and stays there when you scroll further.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/vYObLja/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <h4 id="browser-support" style="position:relative;"><a href="#browser-support" aria-label="browser support permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Browser Support</h4> <p>The <code class="language-text">sticky</code> value is <a href="https://caniuse.com/#feat=css-sticky">not universally supported</a>. </p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/7a8b132259ff823d047250dd77f5f375/93582/sticky-support.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 32%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAABd0lEQVQY0xXKy0vCAADH8f0Z/QEd+weiS8eukY8sLQ0JexCdDDSMKCSiSweJLtVBylf5nM5HJlqzbS5fm3NzG+rBbSbUJkTdmvDhBz/4Al0a4yiEbeEdQpWmGymWqnVadZ5844jSRwWqoSCPpvlKDkdeqljh9TmEFmM0jPAVHJAEUhKoHgcxdGC3uLeRt3Fsgu+ElT4x7FbN+W0HvP81qI8GhNSjhv22yDUEvolR2RqTB6joTePhKgzNeZNT+vDyYtQYyswGUtOx8i2E3mljRhu41vR76sFrHL1E3i/quKcAuxciSyvxdQA9sxfsVhA0x7Nai99kerT4ovPeyIzeZ7A+GXUBgy24WnRs5p22RMYQATWpnCGU0DmTB27IBcifbXlEj0Xme0CfIicO+BCpbZUwzRHsOseOk9R9th2UR21lRCsCMxY7ijCJfwRKBSgiORZJRSJlqQlzYImNV4idcnW5xEbV+zekf4eM2kxIhCISaqmuLJKqf0dVBdHBT1XfAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/7a8b132259ff823d047250dd77f5f375/c54d4/sticky-support.webp 175w, /static/7a8b132259ff823d047250dd77f5f375/a3432/sticky-support.webp 350w, /static/7a8b132259ff823d047250dd77f5f375/426ac/sticky-support.webp 700w, /static/7a8b132259ff823d047250dd77f5f375/c139f/sticky-support.webp 1050w, /static/7a8b132259ff823d047250dd77f5f375/7f403/sticky-support.webp 1400w, /static/7a8b132259ff823d047250dd77f5f375/bc816/sticky-support.webp 1524w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/7a8b132259ff823d047250dd77f5f375/4edbd/sticky-support.png 175w, /static/7a8b132259ff823d047250dd77f5f375/13ae7/sticky-support.png 350w, /static/7a8b132259ff823d047250dd77f5f375/8c557/sticky-support.png 700w, /static/7a8b132259ff823d047250dd77f5f375/e996b/sticky-support.png 1050w, /static/7a8b132259ff823d047250dd77f5f375/2cefc/sticky-support.png 1400w, /static/7a8b132259ff823d047250dd77f5f375/93582/sticky-support.png 1524w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/7a8b132259ff823d047250dd77f5f375/8c557/sticky-support.png" alt="Sticky browser support" title="Sticky browser support" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>For example, Internet Explored does not support it at all. With Safari, you can use it, but you need to use vendor-specific prefixed variant <code class="language-text">position: -webkit-sticky;</code>. Therefore for maximum compatibility, you should use both:</p> <div class="gatsby-code-button-container" data-toaster-id="14056639133975235000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`position: sticky; position: -webkit-sticky;`, `14056639133975235000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">position</span><span class="token punctuation">:</span> sticky<span class="token punctuation">;</span> <span class="token property">position</span><span class="token punctuation">:</span> -webkit-sticky<span class="token punctuation">;</span></code></pre></div> <p>If you need to support IE as well, you can use a polyfill such as <a href="https://github.com/yowainwright/stickybits">StickyBits</a> or <a href="https://github.com/wilddeer/stickyfill">stickyfill</a>.</p> <h2 id="z-index" style="position:relative;"><a href="#z-index" aria-label="z index permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>z-index</h2> <p>When working with <code class="language-text">position</code> other than <code class="language-text">static</code>, elements can easily appear in position, where they overlap each other.</p> <p>In such cases, it is handy to have control over the order of these elements. That is - which elements should be displayed in front and which in back.</p> <p>Fortunately, this is quite easy with a property called <code class="language-text">z-index</code>. It controls the position of your elements on the z-axis. That is, which ones are in front of other elements and which ones are in the background.</p> <p>You can use <code class="language-text">z-index</code> like this:</p> <div class="gatsby-code-button-container" data-toaster-id="43210719556788450000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`z-index: 1;`, `43210719556788450000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span></code></pre></div> <p>The elements with higher <code class="language-text">z-index</code> are displayed in front of elements with lower <code class="language-text">z-index</code>.</p> <!-- TODO this can be replaced by simple url once there is support in the gatsby codepen plugin --> <iframe height="400" scrolling="no" src="//codepen.io/vojtechruz/embed/preview/abOPmwy/?height=400&amp;```&amp;default-tab="result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;"></iframe> <p>In the example, you can see that you can also specify negative numbers, which places these elements behind those with no <code class="language-text">z-index</code> or index with value 0.</p> <h2 id="summary" style="position:relative;"><a href="#summary" aria-label="summary permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Summary</h2> <p>Let's sum it up. The <code class="language-text">position</code> property allows you to determine how elements should be placed on the page. </p> <p>You can define the exact location using <code class="language-text">top</code>, <code class="language-text">bottom</code>, <code class="language-text">right</code> and <code class="language-text">left</code> properties.</p> <p>In case your elements overlap each other, you can change their order using <code class="language-text">z-index</code>. The higher the index, the closer is the element to the user.</p> <p><strong>static</strong></p> <ul> <li>Default value</li> <li>Positioning as usual, same as if you didn't specify the position</li> </ul> <p><strong>relative</strong></p> <ul> <li>The element is placed relative to its normal position on the page</li> <li>The place occupied by the element is preserved at its original location</li> </ul> <p><strong>absolute</strong></p> <ul> <li>The element is removed from the normal flow and does not occupy space</li> <li>The location is determined relative to the first parent set position (other than <code class="language-text">static</code>)</li> <li>If there is no such parent, it is determined relative to the whole page</li> </ul> <p><strong>fixed</strong></p> <ul> <li>The element is removed from the normal flow and does not occupy space</li> <li>The location is determined relative to the viewport</li> <li>Elements keep their locations as you scroll</li> </ul> <p><strong>sticky</strong></p> <ul> <li>Elements are positioned relatively until you reach them by scrolling</li> <li>Then the elements stick to one location, similar to fixed positioning</li> <li>Not supported by all the browsers, you can use polyfills</li> </ul>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Java Records (JEP 359)]]>https://www.vojtechruzicka.com/java-records/https://www.vojtechruzicka.com/java-records/Mon, 03 Feb 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/ef07de7c6de6a81305fa8d40a042080d/b17f8/java-records.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABQBAQAAAAAAAAAAAAAAAAAAAAL/2gAMAwEAAhADEAAAAU180w5o4If/xAAZEAADAQEBAAAAAAAAAAAAAAABAgMREjH/2gAIAQEAAQUCkE5tnQk5DeJJXkJLn//EABcRAAMBAAAAAAAAAAAAAAAAAAECERD/2gAIAQMBAT8BL0SZ/8QAGBEAAgMAAAAAAAAAAAAAAAAAAQIQESH/2gAIAQIBAT8BC7cf/8QAHRAAAgEEAwAAAAAAAAAAAAAAABEhARASIjFBof/aAAgBAQAGPwJ15NRr22faJk//xAAbEAEAAwEAAwAAAAAAAAAAAAABABEhMUFRYf/aAAgBAQABPyFJpX23kfz+YTGX2CYOGzuRKhTdLtn/2gAMAwEAAgADAAAAEP8AL//EABYRAAMAAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPxBCZFP/xAAWEQADAAAAAAAAAAAAAAAAAAAQESH/2gAIAQIBAT8QRQf/xAAcEAEAAwACAwAAAAAAAAAAAAABABEhQWFxgaH/2gAIAQEAAT8QQjjIP1kSXSNDYvUOAaGh6WAqaNyP1SVo2NdPiV1jLs4n/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/ef07de7c6de6a81305fa8d40a042080d/c54d4/java-records.webp 175w, /static/ef07de7c6de6a81305fa8d40a042080d/a3432/java-records.webp 350w, /static/ef07de7c6de6a81305fa8d40a042080d/426ac/java-records.webp 700w, /static/ef07de7c6de6a81305fa8d40a042080d/c139f/java-records.webp 1050w, /static/ef07de7c6de6a81305fa8d40a042080d/7f403/java-records.webp 1400w, /static/ef07de7c6de6a81305fa8d40a042080d/fad48/java-records.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/ef07de7c6de6a81305fa8d40a042080d/e52aa/java-records.jpg 175w, /static/ef07de7c6de6a81305fa8d40a042080d/70ebb/java-records.jpg 350w, /static/ef07de7c6de6a81305fa8d40a042080d/29d31/java-records.jpg 700w, /static/ef07de7c6de6a81305fa8d40a042080d/9ecec/java-records.jpg 1050w, /static/ef07de7c6de6a81305fa8d40a042080d/d165a/java-records.jpg 1400w, /static/ef07de7c6de6a81305fa8d40a042080d/b17f8/java-records.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/ef07de7c6de6a81305fa8d40a042080d/29d31/java-records.jpg" alt="Java Records" title="Java Records" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <h2 id="the-problem" style="position:relative;"><a href="#the-problem" aria-label="the problem permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>The problem</h2> <p>One of the issues with Java is its verbosity and the amount of boilerplate code needed. That's nothing new. </p> <p>Let's consider a simple <code class="language-text">Cat</code> class in Java. We want each cat to have:</p> <ul> <li>Name</li> <li>Number of lives</li> <li>Color</li> </ul> <p>Quite simple, right? Now let's look at the code in Java. For simplicity, let's make our class immutable - no setters, we'll set up everything in our constructor.</p> <div class="gatsby-code-button-container" data-toaster-id="31573402281875616000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public final class Cat { private final String name; private final int numberOfLives; private final String color; public Cat(String name, int numberOfLives, String color) { this.name = name; this.numberOfLives = numberOfLives; this.color = color; } public String getName() { return name; } public int getNumberOfLives() { return numberOfLives; } public String getColor() { return color; } }`, `31573402281875616000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">Cat</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">String</span> color<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>numberOfLives <span class="token operator">=</span> numberOfLives<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> name<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">getNumberOfLives</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> numberOfLives<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getColor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> color<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>It's already quite long, isn't it? It gets worse. We'll also want to have some basic implementation of <code class="language-text">equals()</code> and <code class="language-text">hashCode()</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="23027101358226743000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Cat cat = (Cat) o; return numberOfLives == cat.numberOfLives && Objects.equals(name, cat.name) && Objects.equals(color, cat.color); } @Override public int hashCode() { return Objects.hash(name, numberOfLives, color); }`, `23027101358226743000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">equals</span><span class="token punctuation">(</span><span class="token class-name">Object</span> o<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">==</span> o<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>o <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> o<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token class-name">Cat</span> cat <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Cat</span><span class="token punctuation">)</span> o<span class="token punctuation">;</span> <span class="token keyword">return</span> numberOfLives <span class="token operator">==</span> cat<span class="token punctuation">.</span>numberOfLives <span class="token operator">&amp;&amp;</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> cat<span class="token punctuation">.</span>name<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>color<span class="token punctuation">,</span> cat<span class="token punctuation">.</span>color<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">Objects</span><span class="token punctuation">.</span><span class="token function">hash</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> numberOfLives<span class="token punctuation">,</span> color<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Are we done yet? Not quite, we'll also need some nice <code class="language-text">toString()</code> method:</p> <div class="gatsby-code-button-container" data-toaster-id="12100454933376104000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@Override public String toString() { return &quot;Cat{&quot; + &quot;name='&quot; + name + '\'' + &quot;, numberOfLives=&quot; + numberOfLives + &quot;, color='&quot; + color + '\'' + '}'; }`, `12100454933376104000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"Cat{"</span> <span class="token operator">+</span> <span class="token string">"name='"</span> <span class="token operator">+</span> name <span class="token operator">+</span> <span class="token string">'\''</span> <span class="token operator">+</span> <span class="token string">", numberOfLives="</span> <span class="token operator">+</span> numberOfLives <span class="token operator">+</span> <span class="token string">", color='"</span> <span class="token operator">+</span> color <span class="token operator">+</span> <span class="token string">'\''</span> <span class="token operator">+</span> <span class="token string">'}'</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>And we're finally done. It's about fifty lines of code! It's quite painful to write (although your IDE can help here) and difficult to read. What's worse, it's hard to find some extra functionality (such as new methods) in all the boilerplate.</p> <p>In these fifty lines, there are only three lines that are actually interesting and bear some information:</p> <div class="gatsby-code-button-container" data-toaster-id="30848572745414440000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`private final String name; private final int numberOfLives; private final String color;`, `30848572745414440000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">String</span> color<span class="token punctuation">;</span></code></pre></div> <p>The rest is just boilerplate, which is predictable and can be automatically generated based on these three lines. Your IDE can do that, and there are tools such as <a href="https://projectlombok.org/features/Data">Lombok</a>, which can do this for you as well.</p> <p>In Java, you often use classes, which just hold data, like our Cat. The implementation is always pretty much the same - a bunch of fields, getters, <code class="language-text">equals()</code>, <code class="language-text">hashCode()</code> and <code class="language-text">toString()</code>. Often it is useful to have them immutable, if possible, which has many benefits. But to write and read such classes is a lot of work as there is a lot of code involved. And it is error-prone. Who knows whether your <code class="language-text">hashCode()</code> and <code class="language-text">equals()</code> code is actually correct?</p> <h2 id="records" style="position:relative;"><a href="#records" aria-label="records permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Records</h2> <p><a href="https://openjdk.java.net/projects/jdk/14/">Java 14</a> tries to solve this issue by introducing a new type called <code class="language-text">Record</code>, it is described by <a href="https://openjdk.java.net/jeps/359">JEP 359: Records (Preview)</a></p> <p>The same 50 lines long class from the example above could be written as a record like this:</p> <div class="gatsby-code-button-container" data-toaster-id="65273349130806130000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public record Cat(String name, int numberOfLives, String color) { }`, `65273349130806130000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></code></pre></div> <p>It's a lot less code, right?</p> <p>The functionality is the same as in our previous example - we have:</p> <ul> <li>an immutable class with three fields</li> <li>Constructor assigning these fields</li> <li>Getters</li> <li>equals(), hashCode() and toString()</li> </ul> <p>To illustrate this better, let's look at the decompiled version of our record.</p> <div class="gatsby-code-button-container" data-toaster-id="25468302453002887000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public final class Cat extends java.lang.Record { private final java.lang.String name; private final int numberOfLives; private final java.lang.String color; public Cat(java.lang.String name, int numberOfLives, java.lang.String color) { /* compiled code */ } public java.lang.String toString() { /* compiled code */ } public final int hashCode() { /* compiled code */ } public final boolean equals(java.lang.Object o) { /* compiled code */ } public java.lang.String name() { /* compiled code */ } public int numberOfLives() { /* compiled code */ } public java.lang.String color() { /* compiled code */ } }`, `25468302453002887000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">Cat</span> <span class="token keyword">extends</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">Record</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">final</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> name<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">final</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> color<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span>java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">int</span> <span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">boolean</span> <span class="token function">equals</span><span class="token punctuation">(</span>java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">Object</span> o<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> <span class="token function">name</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">numberOfLives</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span><span class="token class-name">String</span> <span class="token function">color</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* compiled code */</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>You can see that the code is pretty much the same as our old <code class="language-text">Cat</code>. One notable exception is that getters for the fields generated are not named as usual - instead of <code class="language-text">getColor()</code>, there is just <code class="language-text">color()</code>.</p> <p>Also, the class extends <a href="https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Record.html">java.lang.Record</a>.</p> <p>The <code class="language-text">equals()</code> implementation considers two records to be equal if they are the same Type and have the same values. The <code class="language-text">toString()</code> implementation prints our record like this:</p> <div class="gatsby-code-button-container" data-toaster-id="10483497247599626000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Cat[name=Fluffy, numberOfLives=9, color=White]`, `10483497247599626000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Cat</span><span class="token punctuation">[</span>name<span class="token operator">=</span><span class="token class-name">Fluffy</span><span class="token punctuation">,</span> numberOfLives<span class="token operator">=</span><span class="token number">9</span><span class="token punctuation">,</span> color<span class="token operator">=</span><span class="token class-name">White</span><span class="token punctuation">]</span></code></pre></div> <p>Even though these methods are automatically provided for you, it is possible to override them if necessary.</p> <h2 id="limitations" style="position:relative;"><a href="#limitations" aria-label="limitations permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Limitations</h2> <p>There are some restrictions and limitations of records, which you should be aware of.</p> <ul> <li>Records cannot extend any class, although they can implement interfaces</li> <li>Records cannot be abstract</li> <li>Records are implicitly final; they cannot be inherited from</li> <li>You can declare additional fields in the body of a record, but only if they are static</li> </ul> <h2 id="adding-methods" style="position:relative;"><a href="#adding-methods" aria-label="adding methods permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding methods</h2> <p>Even though records are mostly used just as plain data carriers, you can declare your own methods. Of course, since records are immutable, you cannot change any state, but it can still be useful. For example:</p> <div class="gatsby-code-button-container" data-toaster-id="29782870640082694000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public record Cat(String name, int numberOfLives, String color) { public boolean isAlive() { return numberOfLives >= 0; } }`, `29782870640082694000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isAlive</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> numberOfLives <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>You can also add static methods.</p> <h2 id="custom-constructors" style="position:relative;"><a href="#custom-constructors" aria-label="custom constructors permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Custom constructors</h2> <p>By default, new records contain only a constructor, which requires all the fields of the record as parameters. For example, our cat, which has three fields, needs to be constructed like this:</p> <div class="gatsby-code-button-container" data-toaster-id="89733298474619880000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Cat cat = new Cat(&quot;Fluffy&quot;, 9, &quot;White&quot;);`, `89733298474619880000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Cat</span> cat <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">"Fluffy"</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token string">"White"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>What if some parameters can be optional - if we don't provide them, we can use some default value.</p> <p>In our case, the number of lives for new cats is likely to be always 9. We can create an additional constructor, which accepts only name and color, and the number of lives can be set to 9 as a default value. Of course, the constructor with all three fields still exists and is available.</p> <div class="gatsby-code-button-container" data-toaster-id="36676808209013535000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public record Cat(String name, int numberOfLives, String color) { public Cat(String name, String color) { this(name, 9, color); } }`, `36676808209013535000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> color<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>The all-fields constructor is automatically generated for us. But sometimes you need to perform some custom logic there. Such as input validation. You can declare the all-fields constructor by yourself if you need to:</p> <div class="gatsby-code-button-container" data-toaster-id="96199423229383200000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public record Cat(String name, int numberOfLives, String color) { public Cat(String name,int numberOfLives, String color) { if(numberOfLives < 0) { throw new IllegalArgumentException(&quot;Number of lives cannot be less than 0.&quot;); } if(numberOfLives > 9) { throw new IllegalArgumentException(&quot;Cats cannot have that many lives.&quot;); } this.name = name; this.numberOfLives = numberOfLives; this.color = color; } }`, `96199423229383200000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span><span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>numberOfLives <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Number of lives cannot be less than 0."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span><span class="token punctuation">(</span>numberOfLives <span class="token operator">></span> <span class="token number">9</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Cats cannot have that many lives."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>numberOfLives <span class="token operator">=</span> numberOfLives<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>If you are overriding the constructor with all the fields which record specifies (canonical constructor), you can use a declaration without writing the parameters. They are still available for use, but the code is shorter.</p> <div class="gatsby-code-button-container" data-toaster-id="66478501852627000000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public record Cat(String name, int numberOfLives, String color) { // This is the same as public Cat(String name, int numberOfLives, String color) public Cat { // name, numberOfLives and color available here } }`, `66478501852627000000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">record</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> numberOfLives<span class="token punctuation">,</span> <span class="token class-name">String</span> color<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// This is the same as public Cat(String name, int numberOfLives, String color)</span> <span class="token keyword">public</span> <span class="token class-name">Cat</span> <span class="token punctuation">{</span> <span class="token comment">// name, numberOfLives and color available here</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="runtime-introspection" style="position:relative;"><a href="#runtime-introspection" aria-label="runtime introspection permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Runtime introspection</h2> <p>There are two new methods added to <code class="language-text">java.lang.Class</code>, which have records-related functionality.</p> <p>The first one is called <code class="language-text">isRecord()</code>. It is pretty straightforward, you can just check if something is a record or not:</p> <div class="gatsby-code-button-container" data-toaster-id="8558620167444708000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Cat cat = new Cat(&quot;Fluffy&quot;, 9, &quot;White&quot;); if(cat.getClass().isRecord()) { //... }`, `8558620167444708000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Cat</span> cat <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">"Fluffy"</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token string">"White"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span><span class="token punctuation">(</span>cat<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isRecord</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//...</span> <span class="token punctuation">}</span></code></pre></div> <p>The other one is <code class="language-text">getRecordComponents()</code>. You would call it in the same way as in the example above. It returns a list of <code class="language-text">java.lang.reflect.RecordComponent</code>. It is basically a list of all the fields, which are in the record with information such as:</p> <ul> <li>Name</li> <li>Type</li> <li>Accessor </li> <li>Annotations</li> </ul> <h2 id="try-it-yourself" style="position:relative;"><a href="#try-it-yourself" aria-label="try it yourself permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Try it yourself!</h2> <p>If you want to try this feature yourself, you can already do so even though Java 14 is not out there yet (as of 2/2020).</p> <h3 id="preview-feature" style="position:relative;"><a href="#preview-feature" aria-label="preview feature permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Preview feature</h3> <p>Records functionality is available in Java 14. However, currently only as a preview feature. What does it mean?</p> <blockquote> <p>A preview language or VM feature is a new feature of the Java SE Platform that is fully specified, fully implemented, and yet impermanent. It is available in a JDK feature release to provoke developer feedback based on real-world use; this may lead to it becoming permanent in a future Java SE Platform.</p> <p>Before the next JDK feature release, the feature's "real world" strengths and weaknesses will be evaluated to decide if the feature has a long-term role in the Java SE Platform and, if so, whether it needs refinement. Consequently, the feature may be granted final and permanent status (with or without refinements), or undergo a further preview period (with or without refinements), or else be removed.</p> </blockquote> <p>Such features are shipped in the JDK but are not enabled by default. You need to explicitly enable them to use them. Needless to say, it is not intended for production use, but rather for evaluation and experimentation as it may get removed or heavily changed in a future release.</p> <p>To try this feature yourself, you'll need to have <a href="https://jdk.java.net/14/">JDK 14 installed</a>. </p> <h3 id="intellij-idea-setup" style="position:relative;"><a href="#intellij-idea-setup" aria-label="intellij idea setup permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>IntelliJ IDEA setup</h3> <p>In IntelliJ IDEA, you can enable preview features under <code class="language-text">File → Project Structure</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/8cd2d1a5fbb57d38c75b9fccb1884e11/d073d/idea-records-settings.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 37.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABS0lEQVQY031RwU6DQBTkl40n/8BP8Kgm3kyMJ08eLVWbagEpFFhoKdulywK7W9pKLw5Ue3QOk80mM2/ePEMq3TRbrTdKqigKXdedz+dN02z+hdZaKWXcPI2uHgb3L+GzRYuioJRumm3btt9/2O/3u93uyCfAvRNf3L6dXY/O76zLRw8Yj8e+7zHGVj3gxflaSlnXtexxenTiL9dnOa+lrmqVpqnv+0EQRFggDMHe1E/iBBbLHuliAc6yTPUwrGlAuaRcrXi9Wq9JusRATMaEw+HQ9kD4LqfWQgi9wbbyd+fRp2N7UbzMmZATa/I6HJqDgWmatmMnSUIIieMYWTANylII9CI6FmVZGu8fzsSdzaAWKs/zmBC0jXgIjtiQku5n0bdAEQqpKYVVxlhu2NPQ8SKSslx0NRRHz6oqOvAKD85RL8KDkba/0/FY+gdmTrCrDgE/IwAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/8cd2d1a5fbb57d38c75b9fccb1884e11/c54d4/idea-records-settings.webp 175w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/a3432/idea-records-settings.webp 350w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/426ac/idea-records-settings.webp 700w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/c139f/idea-records-settings.webp 1050w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/37e8d/idea-records-settings.webp 1297w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/8cd2d1a5fbb57d38c75b9fccb1884e11/4edbd/idea-records-settings.png 175w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/13ae7/idea-records-settings.png 350w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/8c557/idea-records-settings.png 700w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/e996b/idea-records-settings.png 1050w, /static/8cd2d1a5fbb57d38c75b9fccb1884e11/d073d/idea-records-settings.png 1297w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/8cd2d1a5fbb57d38c75b9fccb1884e11/8c557/idea-records-settings.png" alt="Idea Preview features" title="Idea Preview features" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>To use records in IntelliJ IDEA, you'll need version <code class="language-text">2020.1</code>and later. As of 2/2020, it is available as <a href="https://www.jetbrains.com/idea/nextversion/">Early Access Program build</a>. Currently, IDEA has basic support for records, but full-fledged support should be available with the release version.</p> <h3 id="manual-compilation" style="position:relative;"><a href="#manual-compilation" aria-label="manual compilation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Manual compilation</h3> <p>Alternatively, if building manually, you need to provide the following params to <code class="language-text">javac</code>:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">javac --release 14 --enable-preview ...</code></pre></div> <p>That is for compile-time. At run-time, you just provide <code class="language-text">--enable-preview</code></p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">java --enable-preview ...</code></pre></div> <h3 id="maven-projects" style="position:relative;"><a href="#maven-projects" aria-label="maven projects permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Maven projects</h3> <p>For Maven builds, you can use the following configuration:</p> <div class="gatsby-code-button-container" data-toaster-id="37306939620652550000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <release>14</release> <compilerArgs> --enable-preview </compilerArgs> <source>14</source> <target>14</target> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>--enable-preview</argLine> </configuration> </plugin> <plugin> <artifactId>maven-failsafe-plugin</artifactId> <configuration> <argLine>--enable-preview</argLine> </configuration> </plugin> </plugins> </build>`, `37306939620652550000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>build</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-compiler-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>release</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>release</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>compilerArgs</span><span class="token punctuation">></span></span> --enable-preview <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>compilerArgs</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>source</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>target</span><span class="token punctuation">></span></span>14<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>target</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-surefire-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>argLine</span><span class="token punctuation">></span></span>--enable-preview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>argLine</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>maven-failsafe-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>argLine</span><span class="token punctuation">></span></span>--enable-preview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>argLine</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>build</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="also-new-in-java-14" style="position:relative;"><a href="#also-new-in-java-14" aria-label="also new in java 14 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Also new in Java 14</h2> <ul> <li><a href="https://www.vojtechruzicka.com/java-pattern-matching-instanceof/">Java Pattern Matching: InstanceOf</a></li> <li><a href="https://www.vojtechruzicka.com/java-enhanced-switch/">Enhanced Switch</a></li> <li><a href="https://www.vojtechruzicka.com/java-text-blocks/">Text Blocks</a></li> </ul>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[IntelliJ IDEA best plugins]]>https://www.vojtechruzicka.com/idea-best-plugins/https://www.vojtechruzicka.com/idea-best-plugins/Tue, 21 Jan 2020 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQCAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAACAQP/2gAMAwEAAhADEAAAAWpsWPLNHin/xAAbEAACAQUAAAAAAAAAAAAAAAAAAQIDEBESIf/aAAgBAQABBQJRHTNGR5bB/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAEDE//aAAgBAwEBPwFTRkf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAgEBPwG1v//EABYQAQEBAAAAAAAAAAAAAAAAABAxAP/aAAgBAQAGPwIjd//EABwQAQACAQUAAAAAAAAAAAAAAAEAETEQIWFxsf/aAAgBAQABPyFHyIVzLMJgppnsjqf/2gAMAwEAAgADAAAAEKj/AP/EABYRAQEBAAAAAAAAAAAAAAAAAAARIf/aAAgBAwEBPxCDUP/EABcRAAMBAAAAAAAAAAAAAAAAAAABESH/2gAIAQIBAT8QbXCz/8QAGhABAAMBAQEAAAAAAAAAAAAAAQARITFBYf/aAAgBAQABPxAbgIlFpueFMCrxitQOWFwqC366xbNI6Ia8VE//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/c41b487310b62750024855feb090b819/c54d4/idea-best-plugins.webp 175w, /static/c41b487310b62750024855feb090b819/a3432/idea-best-plugins.webp 350w, /static/c41b487310b62750024855feb090b819/426ac/idea-best-plugins.webp 700w, /static/c41b487310b62750024855feb090b819/c139f/idea-best-plugins.webp 1050w, /static/c41b487310b62750024855feb090b819/7f403/idea-best-plugins.webp 1400w, /static/c41b487310b62750024855feb090b819/fad48/idea-best-plugins.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/c41b487310b62750024855feb090b819/e52aa/idea-best-plugins.jpg 175w, /static/c41b487310b62750024855feb090b819/70ebb/idea-best-plugins.jpg 350w, /static/c41b487310b62750024855feb090b819/29d31/idea-best-plugins.jpg 700w, /static/c41b487310b62750024855feb090b819/9ecec/idea-best-plugins.jpg 1050w, /static/c41b487310b62750024855feb090b819/d165a/idea-best-plugins.jpg 1400w, /static/c41b487310b62750024855feb090b819/b17f8/idea-best-plugins.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/c41b487310b62750024855feb090b819/29d31/idea-best-plugins.jpg" alt="IDEA best plugins" title="IDEA best plugins" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="key-promoter-x" style="position:relative;"><a href="#key-promoter-x" aria-label="key promoter x permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x/">Key promoter X</a></h2> <p>Using keyboard shortcuts instead of your mouse can greatly increase your productivity. With so many features IDEA offers, it can be intimidating and difficult to learn all the shortcuts. Fortunately, there is a plugin called <a href="/learning-intellij-idea-keyboard-shortcuts/">Key Promoter X</a>, which can help you. </p> <p>The idea is simple: whenever you use your mouse to invoke a command in your IDE, a popup is shown, which tells you what the keyboard shortcut for this action is.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 33.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABEElEQVQY042PW0+DMBSA+f9/Y4axRfaiPpo4gQc0MY6xyDYqUC4dtLTl/uphhEXf/HLSnrbnO22Vb89Nk5EowhhHcYw55/IfQJnyaT6/2bZpGpZlvQKGwblomgaOq5l6BvZvySh3fR8Ege/7CCH3cHh6fND1+y/PG4Zh0iZhGkG49Rrlvu9ftltVVZdLbbVaQbJY3GmattRG1uu1vtk4+73rurudA3cIIW6NFJjO5+PHu418X0oh4MO8lIKLkkHUkgteipnf74el0rVtXMhjKsKiKquWlHVwqRJa46JKWI2IzHnTQdGV5gokYFJKFcYoznIvJEGSk7zAaX6KLii+nCIC4aAsTHNG/8AYI4RkWfYD+k91e5ciCEQAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/7be4fd8849f0d1981efed386554bd7c7/c54d4/key-promoter.webp 175w, /static/7be4fd8849f0d1981efed386554bd7c7/a3432/key-promoter.webp 350w, /static/7be4fd8849f0d1981efed386554bd7c7/426ac/key-promoter.webp 700w, /static/7be4fd8849f0d1981efed386554bd7c7/20d15/key-promoter.webp 844w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/7be4fd8849f0d1981efed386554bd7c7/4edbd/key-promoter.png 175w, /static/7be4fd8849f0d1981efed386554bd7c7/13ae7/key-promoter.png 350w, /static/7be4fd8849f0d1981efed386554bd7c7/8c557/key-promoter.png 700w, /static/7be4fd8849f0d1981efed386554bd7c7/33e10/key-promoter.png 844w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/7be4fd8849f0d1981efed386554bd7c7/8c557/key-promoter.png" alt="Key Promoter X IntelliJ IDEA Plugin" title="Key Promoter X IntelliJ IDEA Plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>It even offers you to assign a keyboard shortcut to an action just performed if there is none yet. After a while, it gets so annoying, that you rather make sure you remember your keyboard shortcuts.</p> <h2 id="snyk" style="position:relative;"><a href="#snyk" aria-label="snyk permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/10972-snyk-vulnerability-scanning/">Snyk</a></h2> <p>Application security is often unfortunately neglected. These days most of the application code comes from third-party libraries and frameworks. You need to make sure you use new versions without security vulnerabilities. There are tools, which can help you with this. One of them is Snyk.</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/snyk-detecting-dependencies-with-known-vulnerabilities/" style="box-shadow: none;">Snyk – Detecting dependencies with known vulnerabilities</a></h4><small class="front-post-info"><span class="front-post-info-date">22 November, 2017</span><div class="post-tags"><ul><li><a href="/tags/security/">#Security</a></li></ul></div></small><div><a class="front-post-image" href="/snyk-detecting-dependencies-with-known-vulnerabilities/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 49.3421%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAAD/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAH/2gAMAwEAAhADEAAAAWa6pokuj//EABoQAAICAwAAAAAAAAAAAAAAAAECAxMAEBT/2gAIAQEAAQUCjd+i5wFkfEAv1//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EAB0QAAEDBQEAAAAAAAAAAAAAAAABAhEDECEiMUH/2gAIAQEABj8CheJJK0jZvo/F/wD/xAAaEAEBAAMBAQAAAAAAAAAAAAABEQAhQRBx/9oACAEBAAE/IWJrgkID73JGoi47wSgsNz3/2gAMAwEAAgADAAAAEEPP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8QZ//EABwQAQEAAgIDAAAAAAAAAAAAAAERACEQMUGh0f/aAAgBAQABPxANtUPLXpxdOQixqEKb79YkzYNgLpIdfMCrekLu3EGUGbOP/9k=" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition-delay: 500ms;"><picture><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/snyk/5e4a3/snyk-logo.jpg 45w, /linked/snyk/e451c/snyk-logo.jpg 90w, /linked/snyk/29fd0/snyk-logo.jpg 180w, /linked/snyk/b3ebb/snyk-logo.jpg 270w, /linked/snyk/8841e/snyk-logo.jpg 360w, /linked/snyk/95b54/snyk-logo.jpg 540w, /linked/snyk/51a58/snyk-logo.jpg 1520w" src="/linked/snyk/29fd0/snyk-logo.jpg" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 500ms ease 0s;"></picture><noscript><picture><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/snyk/5e4a3/snyk-logo.jpg 45w, /linked/snyk/e451c/snyk-logo.jpg 90w, /linked/snyk/29fd0/snyk-logo.jpg 180w, /linked/snyk/b3ebb/snyk-logo.jpg 270w, /linked/snyk/8841e/snyk-logo.jpg 360w, /linked/snyk/95b54/snyk-logo.jpg 540w, /linked/snyk/51a58/snyk-logo.jpg 1520w" src="/linked/snyk/29fd0/snyk-logo.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">How to detect and fix security vulnerabilities in your dependencies using Snyk?</span></div></div> <p>Snyk offers many features. One of them is <a href="/idea-snyk-plugin/">IDE plugin</a>, which can scan your dependencies and detect what security vulnerabilities they contain.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAACMElEQVQozz1Sa4/SQBTt/49RfHxQYWE1uDG6IBogu+uuyZqYwELaAm2BpZ0+6HOmD6YUhOIHbx965uT2zO1MO+feYRYr1XQCywsdDIwK2jAtM2E5/Z/HoUs2muEgzWJsyzTWa92yCfYArutC9H0f43yeAZfadQsBa2BBHMcMx00Hg7Eoiov5QpQkEPP5XNO0yWTCczw/4QVhJggiD1N+AmI2m7EsK8tykiSMbXgask5pWvBPegIeDwfgfr8D5kiAuU4KbLfb7M9Ys+TlIy8uhuz0gRNGvPDAT1lBGk8kRbE1zdPUkrLsWCbJd5VgYj8CF42reuXy2asvL5+3Kk8/PXnRrtR6Vdtz6SYOo00UUWAYbjabbA+lkIkopQwYGo/HfA4wA4YXizlkRqNRkYckCI5lOY4bDgeCICmyDVWELzGmrqhISQ9p+js97o8nGPvjLt4l8S7IAHUlGEYGnxDs+2EQ0PLYoasjZXX96/7ye6/z46p922/f9bs/b3r3d+DZMHxDJ4aRESHsOCHUbPvPNxMQz3bMj7cXZ91qo1+v92pvv70+69Y+3DQNy/BJACfMf0tw/vRLkOzY0E9onSojHanyUkYr5NneUlpKMzHrbN5njgPDLAie50bjkTATVQQXKWSwrcurZRzvaJTQaAtxS3dA0PDaJ2EWc0Fw4AdZxQGUFp69ta6hi+vWm1aj9vV9rfOu2jk/7zab/c8KsiwzWK+JuSYQdQ27TgA3BLYVrv8C192nHjbh8xQAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/42ba740aedda35d4342f7e253a8918a0/c54d4/snyk-tab.webp 175w, /static/42ba740aedda35d4342f7e253a8918a0/a3432/snyk-tab.webp 350w, /static/42ba740aedda35d4342f7e253a8918a0/426ac/snyk-tab.webp 700w, /static/42ba740aedda35d4342f7e253a8918a0/c139f/snyk-tab.webp 1050w, /static/42ba740aedda35d4342f7e253a8918a0/7f403/snyk-tab.webp 1400w, /static/42ba740aedda35d4342f7e253a8918a0/55e7f/snyk-tab.webp 1628w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/42ba740aedda35d4342f7e253a8918a0/4edbd/snyk-tab.png 175w, /static/42ba740aedda35d4342f7e253a8918a0/13ae7/snyk-tab.png 350w, /static/42ba740aedda35d4342f7e253a8918a0/8c557/snyk-tab.png 700w, /static/42ba740aedda35d4342f7e253a8918a0/e996b/snyk-tab.png 1050w, /static/42ba740aedda35d4342f7e253a8918a0/2cefc/snyk-tab.png 1400w, /static/42ba740aedda35d4342f7e253a8918a0/eee07/snyk-tab.png 1628w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/42ba740aedda35d4342f7e253a8918a0/8c557/snyk-tab.png" alt="Snyk IntelliJ IDEA plugin" title="Snyk IntelliJ IDEA plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="presentation-assistant" style="position:relative;"><a href="#presentation-assistant" aria-label="presentation assistant permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7345-presentation-assistant/">Presentation assistant</a></h2> <p>This is a simple plugin, which shows a pop-up notification every time you perform an action in IDEA. It shows what action was performed and what is the keyboard shortcut associated with it.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 20%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAvElEQVQI123O2wqCQBgEYN//gQoiMDHSonOYCZWnymx3Pfyr67a6mF51UcMHczUwCmfgu9Hp6Dm2Zy4vxtq3XQQJoS9MEc6fSFTQtqyV5S9FSgkAeZYXtGBFIXjFSspYxTl/dxGiby7af1Fq0SQkjVEcPAL/EXg37xq6YRSSnBAgKEWdlCZZmQGDTiOb7ziO8T18WWdrOBuO5+OBPhgZI3Wlmra5cBbaVtP3+vQwnWwmHW2n4QzXdc3f/a8P2ZXb2rXeDDwAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/051b70750dae926370634f30f22ccad1/c54d4/presentation-assistant.webp 175w, /static/051b70750dae926370634f30f22ccad1/a3432/presentation-assistant.webp 350w, /static/051b70750dae926370634f30f22ccad1/426ac/presentation-assistant.webp 700w, /static/051b70750dae926370634f30f22ccad1/c139f/presentation-assistant.webp 1050w, /static/051b70750dae926370634f30f22ccad1/7f403/presentation-assistant.webp 1400w, /static/051b70750dae926370634f30f22ccad1/14353/presentation-assistant.webp 1689w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/051b70750dae926370634f30f22ccad1/4edbd/presentation-assistant.png 175w, /static/051b70750dae926370634f30f22ccad1/13ae7/presentation-assistant.png 350w, /static/051b70750dae926370634f30f22ccad1/8c557/presentation-assistant.png 700w, /static/051b70750dae926370634f30f22ccad1/e996b/presentation-assistant.png 1050w, /static/051b70750dae926370634f30f22ccad1/2cefc/presentation-assistant.png 1400w, /static/051b70750dae926370634f30f22ccad1/2f8cb/presentation-assistant.png 1689w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/051b70750dae926370634f30f22ccad1/8c557/presentation-assistant.png" alt="Presentation Assistant" title="Presentation Assistant" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>This can be very useful when <a href="/intellij-idea-tips-tricks-presentations/">presenting</a>, during screencasts, pair programming and so on. Especially when using keyboard shortcuts, it can be difficult to follow what's happening without this plugin.</p> <h2 id="edu-tools" style="position:relative;"><a href="#edu-tools" aria-label="edu tools permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/10081-edutools/">Edu Tools</a></h2> <p>This is a great plugin for coding lessons, workshops, and so on. It allows you to build courses, with a set of tasks, which can be performed directly in your IDE. You can build your own courses or import public courses.</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/idea-edu-tools/" style="box-shadow: none;">IDEA EduTools: Learning directly in your IDE</a></h4><small class="front-post-info"><span class="front-post-info-date">05 May, 2019</span><div class="post-tags"><ul><li><a href="/tags/idea/">#IDEA</a></li></ul></div></small><div><a class="front-post-image" href="/idea-edu-tools/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 62.5556%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAACAf/aAAwDAQACEAMQAAABtuTdjrAv/8QAGRAAAwEBAQAAAAAAAAAAAAAAAQIDEhEx/9oACAEBAAEFArHhays0gcUXbCIBXz//xAAXEQEAAwAAAAAAAAAAAAAAAAAAAREx/9oACAEDAQE/AZ1b/8QAFhEBAQEAAAAAAAAAAAAAAAAAEQAB/9oACAECAQE/AcCL/8QAGRAAAgMBAAAAAAAAAAAAAAAAABEBEDGB/9oACAEBAAY/AiE8O03Nf//EABsQAQADAQADAAAAAAAAAAAAAAEAETEhQVFx/9oACAEBAAE/IV2aZchY9TYE62bSgMP2K7OvI+M//9oADAMBAAIAAwAAABBP/wD/xAAWEQADAAAAAAAAAAAAAAAAAAAAASH/2gAIAQMBAT8QZiYf/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERIf/aAAgBAgEBPxBAdaf/xAAbEAEBAAMBAQEAAAAAAAAAAAABEQAhMUFR8P/aAAgBAQABPxC7SCQ+/phJS4eve4BXlIIxdYM3BAzbICNbwlTpxAz/2Q==" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition-delay: 500ms;"><picture><source srcset="/linked/idea-edutools/54d54/idea-edu-tools.jpg 45w, /linked/idea-edutools/357ff/idea-edu-tools.jpg 90w, /linked/idea-edutools/3dfd4/idea-edu-tools.jpg 180w, /linked/idea-edutools/00260/idea-edu-tools.jpg 270w, /linked/idea-edutools/eecda/idea-edu-tools.jpg 360w, /linked/idea-edutools/4d2f6/idea-edu-tools.jpg 900w" sizes="(max-width: 180px) 100vw, 180px"><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/idea-edutools/54d54/idea-edu-tools.jpg 45w, /linked/idea-edutools/357ff/idea-edu-tools.jpg 90w, /linked/idea-edutools/3dfd4/idea-edu-tools.jpg 180w, /linked/idea-edutools/00260/idea-edu-tools.jpg 270w, /linked/idea-edutools/eecda/idea-edu-tools.jpg 360w, /linked/idea-edutools/4d2f6/idea-edu-tools.jpg 900w" src="/linked/idea-edutools/3dfd4/idea-edu-tools.jpg" alt="" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 500ms ease 0s;"></picture><noscript><picture><source srcset="/linked/idea-edutools/54d54/idea-edu-tools.jpg 45w, /linked/idea-edutools/357ff/idea-edu-tools.jpg 90w, /linked/idea-edutools/3dfd4/idea-edu-tools.jpg 180w, /linked/idea-edutools/00260/idea-edu-tools.jpg 270w, /linked/idea-edutools/eecda/idea-edu-tools.jpg 360w, /linked/idea-edutools/4d2f6/idea-edu-tools.jpg 900w" sizes="(max-width: 180px) 100vw, 180px" /><img loading="lazy" sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/idea-edutools/54d54/idea-edu-tools.jpg 45w, /linked/idea-edutools/357ff/idea-edu-tools.jpg 90w, /linked/idea-edutools/3dfd4/idea-edu-tools.jpg 180w, /linked/idea-edutools/00260/idea-edu-tools.jpg 270w, /linked/idea-edutools/eecda/idea-edu-tools.jpg 360w, /linked/idea-edutools/4d2f6/idea-edu-tools.jpg 900w" src="/linked/idea-edutools/3dfd4/idea-edu-tools.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">Study programming courses directly in your IDE or create your own.</span></div></div> <p>This is available either as a separate plugin or as <a href="https://www.jetbrains.com/education/download/#section=idea">IntelliJ IDEA Edu Edition</a>, which is basically IDEA Community edition with this plugin already bundled.</p> <h2 id="extra-icons" style="position:relative;"><a href="#extra-icons" aria-label="extra icons permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/11058-extra-icons/">Extra icons</a></h2> <p>This is basically an icon pack, which provides new icons for special files such as Travis configuration, <code class="language-text">package.json</code>, lint config, and many more.</p> <p>This can be very useful as you can immediately recognize these special files, and your project is, therefore, much easier to navigate. For example, <code class="language-text">package.json</code> is a json file, but it is much more useful to have a special icon for it as it is an important file for npm projects.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABLklEQVQoz42SSWvDMBSE/f9/SqGXXBMaE0KX0EIhgdppYmu1ZGcRtqzUi+T0uade2ugDCSQYRjNPQds0ES/W6w3nzBijKw2YujY/WOeufxPAsm742r1xgrJMFnmuzurqR2BH+uj9EaV7hBDnHM7DMFgPgjiOoyi6W9yHi3A2nc3DcLlcvrysYg+CdqR5Xj0l+32KUFEUXdfBVdf37S3GzMPgXpMppijZgT6lhDDGa63HWMP1RmFALjDnlBKqlLK9bZrG/dvzL7Htz5OJwJhB3TKHCfm2PWqt3cwfCBohhAohDoejrzPkWuf1Lkk/t1tK6el0gi8C07othraOusSFElIyxihjZVn6Prvv2g95QBgTjIWQhJAsE2B+uVw8nJ2TpldVVZUlbFrXAIiNh/gbXSjnVXasm+QAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/ba38d6865cb9a793e352da1f329958de/c54d4/icons.webp 175w, /static/ba38d6865cb9a793e352da1f329958de/a3432/icons.webp 350w, /static/ba38d6865cb9a793e352da1f329958de/426ac/icons.webp 700w, /static/ba38d6865cb9a793e352da1f329958de/8b983/icons.webp 768w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/ba38d6865cb9a793e352da1f329958de/4edbd/icons.png 175w, /static/ba38d6865cb9a793e352da1f329958de/13ae7/icons.png 350w, /static/ba38d6865cb9a793e352da1f329958de/8c557/icons.png 700w, /static/ba38d6865cb9a793e352da1f329958de/e5715/icons.png 768w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/ba38d6865cb9a793e352da1f329958de/8c557/icons.png" alt="Extra Icons" title="Extra Icons" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="grazie" style="position:relative;"><a href="#grazie" aria-label="grazie permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/12175-grazie/">Grazie</a></h2> <p><strong>UPDATE:</strong> As of 2020.1, Grazie is bundled by default in IntelliJ IDEA.</p> <p>IntelliJ IDEA has a basic spelling checker, which allows you to detect some typos, but it is not much useful on top of that. It is definitely not a replacement for dedicated grammar checking services such as <a href="https://app.grammarly.com/">Grammarly</a>.</p> <p>Fortunately, there is a plugin called Grazie, which enhances the basic spell checker with a lot of useful features. It also detects more advanced issues, such as incorrect grammar. It works completely offline, as well.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 22.857142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAuUlEQVQY012M226DMBBE+f+/q9RIbUMsLo5tzNrr9QMGG4UsSJGgR0ejeZmpvBPj8Af2bvWv7G/m+SPb70Z8LZPKSS8TqzhLMv/Mk65eH7Zty6UYY7RS3U4rD47eRaKU0nQixlhtJ0opRME5eNS1eHDUXds+DxBDXpiZc55nbvx1GeeS7Qh6gFo090PR8FjxWGsdKAKSQwphl+8u43VdrQu99XIMciQFZH30iOD8nj70g5eWCwI4RHwDtsMYXIe3dRsAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/cd2cb1ae4891e045cc8dd0f9686722e5/c54d4/grazie.webp 175w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/a3432/grazie.webp 350w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/426ac/grazie.webp 700w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/c139f/grazie.webp 1050w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/6094c/grazie.webp 1267w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/cd2cb1ae4891e045cc8dd0f9686722e5/4edbd/grazie.png 175w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/13ae7/grazie.png 350w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/8c557/grazie.png 700w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/e996b/grazie.png 1050w, /static/cd2cb1ae4891e045cc8dd0f9686722e5/8c381/grazie.png 1267w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/cd2cb1ae4891e045cc8dd0f9686722e5/8c557/grazie.png" alt="Grazie IntelliJ IDEA plugin example" title="Grazie IntelliJ IDEA plugin example" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>It allows you to enable checking of commit messages as well. You can learn more in the blog post <a href="https://blog.jetbrains.com/idea/2019/11/meet-grazie-the-ultimate-spelling-grammar-and-style-checker-for-intellij-idea/">Meet Grazie: the ultimate spelling, grammar, and style checker for IntelliJ IDEA</a>.</p> <h2 id="maven-helper" style="position:relative;"><a href="#maven-helper" aria-label="maven helper permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7179-maven-helper">Maven Helper</a></h2> <p>IDEA already has powerful tools for <a href="https://www.vojtechruzicka.com/idea-analyzing-dependencies/">analyzing your dependencies</a> and potential problems with them, such as conflicts or cyclic dependencies.</p> <p>Maven helper offers an alternative UI, which shows your dependencies hierarchically. This can be easier to read and navigate. Also, if you're using the community edition, this is your only option.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 60.57142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAAB3klEQVQoz21SSY7UQBD050EjISQ+wgEJxIEL4jRSC9zq9sbY5aW8lvd9d5soe+ZGqJyyXJkZGeEUKHVMQlRVdV03CIKyLKsD5YGiKMr/AQlxHAu8JgzO9LquszxHbNtmHMdh6Ju6blqEujnPG9q2TdNUUDVNkhVN04hJ0ixnLHY9LwjDvCjyPC/Kgoeqrqq65P3Pw5kZmCVFdaiL+34Yh3Eap3le1jQrkjSzbAcniBiKQYoZxmnC/TQvOK7nC5IkQWqSJPi47/vjgbCj17puSRLLsmwChOi6/vKigzM5AF1hFAmieL1cLo5DoWR/A4q7rosYgxzU+74PAjhUHMDM8zwfxdcrusKAdV0P5sdZ3Pc9KG+3m6Iotm3DHtiBFjDizAzDULjLsu04EPw4sG2cOc9LmGwQgxDiez5jDIRd12/btizLScCLLZPELOra15lfLyIGzW3TGIZhccmEUhpyRHjgHtL42M9/bqKk0TAhbphC6bR28+ZHMSzAkLZtQTBEYlngAmrADMvxgp8vfPjy+93ny9NX+eN35f035dNP7+kH/aUG+745lIqiCM/u9zvIz58CDMOA6dBL+Ktphm5gHgDhXEksQMkXrnJdalkmZrYsy/M8TOG5WCIvyzJY+A+D45NHqXfvtAAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/307269d2f389ca11e78506a6b3ce3652/c54d4/maven-helper.webp 175w, /static/307269d2f389ca11e78506a6b3ce3652/a3432/maven-helper.webp 350w, /static/307269d2f389ca11e78506a6b3ce3652/426ac/maven-helper.webp 700w, /static/307269d2f389ca11e78506a6b3ce3652/c139f/maven-helper.webp 1050w, /static/307269d2f389ca11e78506a6b3ce3652/7f403/maven-helper.webp 1400w, /static/307269d2f389ca11e78506a6b3ce3652/2c1a7/maven-helper.webp 1832w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/307269d2f389ca11e78506a6b3ce3652/4edbd/maven-helper.png 175w, /static/307269d2f389ca11e78506a6b3ce3652/13ae7/maven-helper.png 350w, /static/307269d2f389ca11e78506a6b3ce3652/8c557/maven-helper.png 700w, /static/307269d2f389ca11e78506a6b3ce3652/e996b/maven-helper.png 1050w, /static/307269d2f389ca11e78506a6b3ce3652/2cefc/maven-helper.png 1400w, /static/307269d2f389ca11e78506a6b3ce3652/84a78/maven-helper.png 1832w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/307269d2f389ca11e78506a6b3ce3652/8c557/maven-helper.png" alt="Maven Helper IntelliJ IDEA plugin" title="Maven Helper IntelliJ IDEA plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="innerbuilder" style="position:relative;"><a href="#innerbuilder" aria-label="innerbuilder permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7354-innerbuilder/">InnerBuilder</a></h2> <p>Creating complex objects can be hard. Of course, you can stick with a simple POJO with a whole bunch of setters, but you cannot enforce that created objects have all the required fields and are not used in an incomplete state. To solve this, you need constructors. With more complex objects, this can lead to a whole bunch of <a href="https://www.vojtechruzicka.com/avoid-telescoping-constructor-pattern/">telescopic constructors</a> with many parameters. In these cases, using a builder is much more appropriate.</p> <p>This plugin can automatically generate a builder for you from a POJO.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 52.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAAB0UlEQVQoz22Sy2oUQRSGGwwIrgUfwhdza0TcuYgrRXQlPoCbbGaR4CLkghoxKiIJJMJkMn2Z7q6e7rqculdXT5Jpa6IJYvLzUVAFP+ec/1QE0BrjfOvn53OrDapqBtAQiilrMJFKa+uuo4xVSkdJwqdVM00aVgqrNTTIAD41vO9071U/s/2pu4EzpwREcQwVohQxL3xN5cr7o9cbh28/F692sudb6YubeLmdPttIvh5XUWhbSBk6P2v9YYbvPtm6v7J57+n3O493l5Y/LS1/uPXwf24/+hg92H6zM4pIQzmjzkFrKWrq1b2Ttb2jwbeT1S/H73aHgx+T9YNqbR/9y/o+GvxEv7IqogQ4YGe5tcA5nhZZkY5RNq4mCavLc6/n3lyn76ziLCKklbIN0sYRAkmWZZNJnudcSCG10iYEfoWQ6grGIKLUKdUaq0EaQkSaJnEcJ0mSpmlZlvhSJEApY0yrvwKAEJiT0prF3iwXLHiCsyiKPJ8gVNJLBdsfs7ior8NSg5kxy7lw1s5mXbiPRqPhcJgX+bRugAJQHh4ZZSEYrRUIeTCuxkUThqvrOlKqC8ZuoVloZlFl8b0IJhwaybHgIUaMQwJd54NciMf7i8P/Bkq+QWQjY4lvAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/3a8c924b398f23858473232b409b188d/c54d4/inner-builder-generator.webp 175w, /static/3a8c924b398f23858473232b409b188d/a3432/inner-builder-generator.webp 350w, /static/3a8c924b398f23858473232b409b188d/426ac/inner-builder-generator.webp 700w, /static/3a8c924b398f23858473232b409b188d/c139f/inner-builder-generator.webp 1050w, /static/3a8c924b398f23858473232b409b188d/7f403/inner-builder-generator.webp 1400w, /static/3a8c924b398f23858473232b409b188d/b4689/inner-builder-generator.webp 2304w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/3a8c924b398f23858473232b409b188d/4edbd/inner-builder-generator.png 175w, /static/3a8c924b398f23858473232b409b188d/13ae7/inner-builder-generator.png 350w, /static/3a8c924b398f23858473232b409b188d/8c557/inner-builder-generator.png 700w, /static/3a8c924b398f23858473232b409b188d/e996b/inner-builder-generator.png 1050w, /static/3a8c924b398f23858473232b409b188d/2cefc/inner-builder-generator.png 1400w, /static/3a8c924b398f23858473232b409b188d/cb93d/inner-builder-generator.png 2304w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/3a8c924b398f23858473232b409b188d/8c557/inner-builder-generator.png" alt="InnerBuilder plugin for IntelliJ IDEA" title="InnerBuilder plugin for IntelliJ IDEA" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="rainbow-brackets" style="position:relative;"><a href="#rainbow-brackets" aria-label="rainbow brackets permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets/">Rainbow Brackets</a></h2> <p>If you are struggling with a lot of nested blocks and a lot of brackets, this plugin is for you. It makes it very easy to locate matching brackets as each pair of brackets is color-coded.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 71.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAABZUlEQVQoz42SWY7bQAxEfZw0t973luwgSO5/pFCWAc8MBgMRDelD/VjFEm/F0YyYAo1Cq4WaueTQx0rFE4sVJyxExKxvRiL8UDdAtEzF4z1j8TbmOsYKqYxWZw4lVOWJyYfgnAVkRHjD2ssgadMZaUToJfd1jzmr4tZrCCG6HGxkJmfFeo/KA5wdbqhGtIEaI2wea61trtZ7SHlsjz632ve59lLabFvOTaz1IdqQdKY3rI9gKdW+tn2OXNuY69HGUmpMDWHNde9jeicSCjmn4BvWYwBq7Y8//9b+aDWPklPKwuy9T7mkXK0yh+fX2C+YnjAwO+dSqSKW0XgBeN6Ds4wBgE9pv5Q1BhL9YswvPWfvTxe/qyPtU1lEcWudP7wR44U6YDiiJie4z/r39z2Wchi5An+wzY6MHKkZvFZPZT5XB5+B0Jcd/FFZRQmjRSvsfLA6+uW6qaTuZnXgreif1B1EgIvwfzv3mCjaxwIhAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/0bffdb02bb4329a8e9eeb4f955f8ea0e/c54d4/rainbow-brackets.webp 175w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/a3432/rainbow-brackets.webp 350w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/426ac/rainbow-brackets.webp 700w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/c139f/rainbow-brackets.webp 1050w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/4f5bc/rainbow-brackets.webp 1162w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/0bffdb02bb4329a8e9eeb4f955f8ea0e/4edbd/rainbow-brackets.png 175w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/13ae7/rainbow-brackets.png 350w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/8c557/rainbow-brackets.png 700w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/e996b/rainbow-brackets.png 1050w, /static/0bffdb02bb4329a8e9eeb4f955f8ea0e/84bf8/rainbow-brackets.png 1162w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/0bffdb02bb4329a8e9eeb4f955f8ea0e/8c557/rainbow-brackets.png" alt="Rainbow Brackets IntelliJ IDEA Plugin" title="Rainbow Brackets IntelliJ IDEA Plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>It is quite hard to read with the default color theme, so Darcula is much more suitable for use with this plugin.</p> <h2 id="indent-rainbow" style="position:relative;"><a href="#indent-rainbow" aria-label="indent rainbow permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/13308-indent-rainbow/">Indent Rainbow</a></h2> <p>This plugin is similar to Rainbow brackets, but it does not color your brackets, but rather your indentation. It can be beneficial if you have a lot of indentation levels, which are harder to navigate. It can even mark lines, which don't mark proper indentation levels with red, so it is immediately obvious when there is a mismatch.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 62.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAABN0lEQVQoz41SW27DMAzLcZpYD8uWX3KaYPe/1NSuwz6aASVsf4kiRXmpFYZC5qAJRo0lY0q5VEUmREZAAEB8vOENy5xQpgyVe4ZZuLTWutXWrOUqSuAtQCR6g+2dbAZi0pNZjj1vvdU+DxEZTXtrmqqIRo45aUBy9acF+CMnJ+eRXKxwdV0bRbXb3ebZ52lz9jb2ccQokpQku4tf2x20R6fm0muhaXacx3535mHuJufW6pj7sD2SJ8ABXfY1wfIV17vAiXGPMgn2EMbYXUmYasKnT3wGhuFt6KV3kC41dxJdIay327auj7Ntfn+Kticu0hYBShhZGAIxP6Zivii8wiuwkU217FbOOSjGT8m+5zRFU0cUdx3cbPgUS0rAmZF8hxsghauf9C+5NWClEkOiUGpj4stsLvENHHGE506oEEwAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/6d9b5a38e105129b10a8d5102d8be623/c54d4/rainbow-indent.webp 175w, /static/6d9b5a38e105129b10a8d5102d8be623/a3432/rainbow-indent.webp 350w, /static/6d9b5a38e105129b10a8d5102d8be623/426ac/rainbow-indent.webp 700w, /static/6d9b5a38e105129b10a8d5102d8be623/c139f/rainbow-indent.webp 1050w, /static/6d9b5a38e105129b10a8d5102d8be623/2c416/rainbow-indent.webp 1367w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/6d9b5a38e105129b10a8d5102d8be623/4edbd/rainbow-indent.png 175w, /static/6d9b5a38e105129b10a8d5102d8be623/13ae7/rainbow-indent.png 350w, /static/6d9b5a38e105129b10a8d5102d8be623/8c557/rainbow-indent.png 700w, /static/6d9b5a38e105129b10a8d5102d8be623/e996b/rainbow-indent.png 1050w, /static/6d9b5a38e105129b10a8d5102d8be623/f0811/rainbow-indent.png 1367w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/6d9b5a38e105129b10a8d5102d8be623/8c557/rainbow-indent.png" alt="Indent Rainbow IntelliJ IDEA Plugin" title="Indent Rainbow IntelliJ IDEA Plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Again, this is much less visible in the light theme, so Darcula may be a better choice. </p> <h2 id="string-manipulation" style="position:relative;"><a href="#string-manipulation" aria-label="string manipulation permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/2162-string-manipulation/">String Manipulation</a></h2> <p>This is a handy plugin, which allows you to perform various actions with your strings such as:</p> <ul> <li>Escaping/un-escaping for various contexts (Java, HTML, XML, SQL, ...)</li> <li>Case switching (lower, upper, kebab, pascal, camel, ...)</li> <li>Sorting</li> <li>Grep</li> <li>Trimming</li> </ul> <h2 id="git-toolbox" style="position:relative;"><a href="#git-toolbox" aria-label="git toolbox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7499-gittoolbox/">Git Toolbox</a></h2> <p>Although git support in IntelliJ is already pretty good, this plugin still offers some useful features on top of that.</p> <p>You can easily see how many commits is your branch ahead/behind. You can configure automatic fetch with configurable frequency. You can automatically see inline blame information on your current line. </p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 36.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAAwUlEQVQY042QyxKDIAxF/f8PtIgPFCIPFdRW973C6HRqFz2LTIYk95Jk1OuccUW9UopzboypqrppxBKZE2FGvt7IpBnzmsiOwXu0OefsgUH+1brcyNDciloIEcKMulJktDZGP/8g01ozxsqy5EXxOGDe+33fYYXyp/OP4cHZrmullEpKIoIK5OLPbQghrX3F18npPK2VkJgXkXgzC5UuauGKFG8JXGQYBsRt247heX36EC96Agd/Ml34CSW8pJi2eANpNoGvQyeB5AAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/8b6f160676dd83ebcb9123398912e36a/c54d4/git-toolbox.webp 175w, /static/8b6f160676dd83ebcb9123398912e36a/a3432/git-toolbox.webp 350w, /static/8b6f160676dd83ebcb9123398912e36a/426ac/git-toolbox.webp 700w, /static/8b6f160676dd83ebcb9123398912e36a/c139f/git-toolbox.webp 1050w, /static/8b6f160676dd83ebcb9123398912e36a/26598/git-toolbox.webp 1160w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/8b6f160676dd83ebcb9123398912e36a/4edbd/git-toolbox.png 175w, /static/8b6f160676dd83ebcb9123398912e36a/13ae7/git-toolbox.png 350w, /static/8b6f160676dd83ebcb9123398912e36a/8c557/git-toolbox.png 700w, /static/8b6f160676dd83ebcb9123398912e36a/e996b/git-toolbox.png 1050w, /static/8b6f160676dd83ebcb9123398912e36a/6be49/git-toolbox.png 1160w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/8b6f160676dd83ebcb9123398912e36a/8c557/git-toolbox.png" alt="Git Toolbox" title="Git Toolbox" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="request-mapper" style="position:relative;"><a href="#request-mapper" aria-label="request mapper permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/9567-request-mapper">Request Mapper</a></h2> <p>IDEA already offers pretty powerful navigation among files. When working with REST endpoints, you may sometimes prefer to navigate to a specific mapping (such as <code class="language-text">/persons</code>) instead of class (such as <code class="language-text">PersonController</code>). This is in cases where you know the URL, but don't remember which class or method it corresponds to. After pressing the associated keyboard shortcut, you can search your URL mappings and navigate to them directly.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 25.71428571428572%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAA8ElEQVQY033IUU+DMBDAcb7/qx9AplmMMUYffFuMRpY4SKcbuKEZDTC4UtZRHRBLKQ7rjK/+87u75Ay+ZRkkdfkhW9XKRknxoxFSfHaq7f/NMO8WR6NX8zG5Qd7QjgYTOLVhiLITB84QuZzR82l28USvPXblbvT/eEIGNjEPjPGzb3n4FvkWmt2j5ch5eZgHlhuMPYxW4Lyl2jTI3Jh568LPqiUpF7DTWzMivMohSSIch+E6xBrEIYVEKzaUs/yd5TvORF12UuyV7L/avtNb9XtlEEIopeRQmqYAQPOcc87YtiiK6re61qNvWVZNI9Vf35b1CxBVqM7dAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/09e7c6f09a993f0bf37620f5ae8fd465/c54d4/request-mapper.webp 175w, /static/09e7c6f09a993f0bf37620f5ae8fd465/a3432/request-mapper.webp 350w, /static/09e7c6f09a993f0bf37620f5ae8fd465/426ac/request-mapper.webp 700w, /static/09e7c6f09a993f0bf37620f5ae8fd465/c139f/request-mapper.webp 1050w, /static/09e7c6f09a993f0bf37620f5ae8fd465/7f403/request-mapper.webp 1400w, /static/09e7c6f09a993f0bf37620f5ae8fd465/eccbe/request-mapper.webp 1897w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/09e7c6f09a993f0bf37620f5ae8fd465/4edbd/request-mapper.png 175w, /static/09e7c6f09a993f0bf37620f5ae8fd465/13ae7/request-mapper.png 350w, /static/09e7c6f09a993f0bf37620f5ae8fd465/8c557/request-mapper.png 700w, /static/09e7c6f09a993f0bf37620f5ae8fd465/e996b/request-mapper.png 1050w, /static/09e7c6f09a993f0bf37620f5ae8fd465/2cefc/request-mapper.png 1400w, /static/09e7c6f09a993f0bf37620f5ae8fd465/10b63/request-mapper.png 1897w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/09e7c6f09a993f0bf37620f5ae8fd465/8c557/request-mapper.png" alt="Request Mapper" title="Request Mapper" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>The plugin currently supports Spring, JAX-RS, and Micronaut.</p> <h2 id="multirun" style="position:relative;"><a href="#multirun" aria-label="multirun permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7248-multirun/">Multirun</a></h2> <p>IDEA <a href="https://www.jetbrains.com/help/rider/Creating_Compound_Run_Debug_Configuration.html">does support running multiple tasks (configurations) at once</a>, however, this feature is currently very limited.</p> <p>Multirun plugin does support running multiple configurations at once with much more sophisticated options. This can be very useful when running both Backend and Frontend at once, testing multiple modules, and so on.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 48%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABN0lEQVQoz4WRy1KDQBBF599dmLhyYfklLhKt0r8wCYRkXgwQXj0v4tKboVxYieWpS9eF7mbohtWJEC9Mt3DOoaBMtG1r2lGXBt4YwxqjjZLdqXZj/xX9TUVHnsZgB5joXbBjoAGRfR7N5qjL3uveyw5yV7o8V32YpYdkOq87xx5Wu8e37Om9WK6y+1W+fC0W6/1yvb+OSKHg7mWzSP75o2CCcym4kkIJUVdmiuE8xTNiMriNwU/Bx6Sh76qydJbgvSWWZdnhcMzzPNvtttstPOCc44KRUmI3Simsx1rrvcdesUJ4RIa0UlpJHMyFkCKBavQg1TQnIhoB0aXnp22G4ZVVZerfWEs+MR/yF0xqU5pal1VVn0zV9AP5MBF+hwtztC7e1Ggdw2DFAZPyfVFgWIjo8mX/y7pvgHcdHW1yNzwAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e78d50ca881bef0d134a58fe85717833/c54d4/multirun.webp 175w, /static/e78d50ca881bef0d134a58fe85717833/a3432/multirun.webp 350w, /static/e78d50ca881bef0d134a58fe85717833/426ac/multirun.webp 700w, /static/e78d50ca881bef0d134a58fe85717833/c139f/multirun.webp 1050w, /static/e78d50ca881bef0d134a58fe85717833/7f403/multirun.webp 1400w, /static/e78d50ca881bef0d134a58fe85717833/83d4c/multirun.webp 1482w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/e78d50ca881bef0d134a58fe85717833/4edbd/multirun.png 175w, /static/e78d50ca881bef0d134a58fe85717833/13ae7/multirun.png 350w, /static/e78d50ca881bef0d134a58fe85717833/8c557/multirun.png 700w, /static/e78d50ca881bef0d134a58fe85717833/e996b/multirun.png 1050w, /static/e78d50ca881bef0d134a58fe85717833/2cefc/multirun.png 1400w, /static/e78d50ca881bef0d134a58fe85717833/a83dd/multirun.png 1482w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/e78d50ca881bef0d134a58fe85717833/8c557/multirun.png" alt="Multirun" title="Multirun" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="idea-mind-map" style="position:relative;"><a href="#idea-mind-map" aria-label="idea mind map permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/8045-idea-mind-map/">IDEA Mind Map</a></h2> <p>It can be very useful to have a mind map editor and viewer directly integrated in your IDE. Not only for quick brainstorming, but also for documentation. The plugin works with <code class="language-text">MMD</code> files, which are text-based, so it is very easy to have your mind maps under version control and manage their changes.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 51.42857142857144%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAACD0lEQVQoz01SW28SURDef9MfYfoXjPGtyE0oUvSlFljY3S4LChQW2Bt7uC1doCwtNKDR+ND0xRefSE1pcREsl2LXRmP8D84W05hMJnPOnO+bOd8M5gyLphESeBeZd+/K7t28i8o7woIjLK48ZJ+SEsSpSpdTGrVOL7ffTVe7mD0k2nDehgu2kBDLaxmlw1bajHgAFCtGAFsCuQ0/Zw1y9d5JsXlsGOPbH8svkzn2PFbcSSrbycrLZAVnlRCrEJmqf09xU/lNuuAkRA8tv0atOGpFhIOS9m4nVR1+7v+8vRmMphiZq2crnb3iISPUEqiZkJvpcjuK2m5KchKCHecATPF1mq8H00q9dwrvz8fXhmEM9CsMuO0hwUMjvvYG+rTiArQHrT700GsPHq2tP7YEspYgt+HPwn21/QFnVV2/vFkuzi7HAJbgt16m2Hp7GkirAVYNsvvbScUe4qz+lNWfdhCilylsRUvPIoiRtEBKPf86ms2uVpUls3IElVvvE4XDBNLicpPKqZum7AgwXgZlq8eZ8hHX6LkoyRctXUzGi8V8oH+7r1xo9k5AJJAduEwzhyTAzICFzNXIrErzja0I8sXKs6n+/XrevxhhDkJaTdJUiASR/h1Bi1UKBIvLWhxpoKWPkX2x0nSx/PP713A8w+43AardxeL/63HnhSdBDsxqev7Fq9LH/lCfTD+dDf8CjetMLMwrXvoAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/fcb39f0fc231237ce9b25a4d91fb9f2c/c54d4/mind-map.webp 175w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/a3432/mind-map.webp 350w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/426ac/mind-map.webp 700w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/c139f/mind-map.webp 1050w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/7f403/mind-map.webp 1400w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/a28dd/mind-map.webp 1887w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/fcb39f0fc231237ce9b25a4d91fb9f2c/4edbd/mind-map.png 175w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/13ae7/mind-map.png 350w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/8c557/mind-map.png 700w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/e996b/mind-map.png 1050w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/2cefc/mind-map.png 1400w, /static/fcb39f0fc231237ce9b25a4d91fb9f2c/be1b7/mind-map.png 1887w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/fcb39f0fc231237ce9b25a4d91fb9f2c/8c557/mind-map.png" alt="IDEA Mind Map" title="IDEA Mind Map" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="codota-and-tabnine" style="position:relative;"><a href="#codota-and-tabnine" aria-label="codota and tabnine permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7638-codota">Codota</a> and <a href="https://plugins.jetbrains.com/plugin/12798-tabnine/">TabNine</a></h2> <p>These two plugins are similar. They enhance your autocomplete based on scanning open-source projects and intelligently predicting what you probably want to type based on the context.</p> <p>With Codota, you can also look up relevant examples of some class or method in public projects, which can be handy.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 49.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABbklEQVQoz3VRa2+bQBC8//+7qlZK5CZWH8HGIA77YnC49wtzmHQOp/2Ujkar3WNv92YgP8rTnp5Pp6OQ0lijjVFKS6WVBnOp9QelyuU/WGtI3bSv/UUpaY3xzoFCWbDj6tQL1su2E+wiwX6QMs/TCt1ScanI5qn59nh83Fab3/XzjoLbXYucssH7PO0Oa20IAaW1LoYYx6t2gRgdBA9Cai7lGxdc6cvA+2F4KXZFUVTVgdKGNseiKKuqqusakXOOWVJKklJ6/wwtpeV+fzgccIGxjvGuoR1jr9g/z/OyLCnNBFkMCfuzNbDFGiEEVDnn9BqB4D0+OR+UVHg2ShiGcyLgoQ2gsTH46H38KxM9wa2yEa1z97jSG+uUduRp2z5s2Jfvzdef5cOv8vmFcuhef4XKwY7jdcy4xw/EccS+rDn46XzWvXjjWtjV3xgDOrB6mq7v/8FyWwiUpDlB+XxbUN9uC7KV+QiuTFP6lHjLH6HZM0kiEErMAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/14e3b1b0aefca8bf4a109aea1d06f142/c54d4/codota.webp 175w, /static/14e3b1b0aefca8bf4a109aea1d06f142/a3432/codota.webp 350w, /static/14e3b1b0aefca8bf4a109aea1d06f142/426ac/codota.webp 700w, /static/14e3b1b0aefca8bf4a109aea1d06f142/c139f/codota.webp 1050w, /static/14e3b1b0aefca8bf4a109aea1d06f142/7f403/codota.webp 1400w, /static/14e3b1b0aefca8bf4a109aea1d06f142/3abdb/codota.webp 2239w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/14e3b1b0aefca8bf4a109aea1d06f142/4edbd/codota.png 175w, /static/14e3b1b0aefca8bf4a109aea1d06f142/13ae7/codota.png 350w, /static/14e3b1b0aefca8bf4a109aea1d06f142/8c557/codota.png 700w, /static/14e3b1b0aefca8bf4a109aea1d06f142/e996b/codota.png 1050w, /static/14e3b1b0aefca8bf4a109aea1d06f142/2cefc/codota.png 1400w, /static/14e3b1b0aefca8bf4a109aea1d06f142/e8212/codota.png 2239w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/14e3b1b0aefca8bf4a109aea1d06f142/8c557/codota.png" alt="Codota relevant examples" title="Codota relevant examples" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="nyan-cat-progress-bar" style="position:relative;"><a href="#nyan-cat-progress-bar" aria-label="nyan cat progress bar permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/8575-nyan-progress-bar">Nyan cat progress bar</a></h2> <p>Want to make waiting when idea is loading or building indices more fun? You can replace IDEA progress bars with a <a href="https://www.youtube.com/watch?v=QH2-TGUlwu4">Nyan cat</a> version.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 18.857142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAAmklEQVQI14WOywrCMBBF8//gygo+EHys/Qg3IuJGbRuhm9i0mUxq0jwWbh0K7hQPw+EyMNxh2Dk0znROowVtAFRZFOUAL/mdc3J+y6WUiKg/AACZyeqkxKWuzqa5Jgu98973hP9NjDGlRGbLbLSdT9bT8WaR7War4/7QqlYI8fgG7akzhEC1dM8afLXDUFC6t/YZQgz/oNeo+Q29PNVC1+ZODwAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/4148092ad044bfb2dbf736a113a6d8ac/c54d4/nyan-cat.webp 175w, /static/4148092ad044bfb2dbf736a113a6d8ac/a3432/nyan-cat.webp 350w, /static/4148092ad044bfb2dbf736a113a6d8ac/426ac/nyan-cat.webp 700w, /static/4148092ad044bfb2dbf736a113a6d8ac/c139f/nyan-cat.webp 1050w, /static/4148092ad044bfb2dbf736a113a6d8ac/71a33/nyan-cat.webp 1109w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/4148092ad044bfb2dbf736a113a6d8ac/4edbd/nyan-cat.png 175w, /static/4148092ad044bfb2dbf736a113a6d8ac/13ae7/nyan-cat.png 350w, /static/4148092ad044bfb2dbf736a113a6d8ac/8c557/nyan-cat.png 700w, /static/4148092ad044bfb2dbf736a113a6d8ac/e996b/nyan-cat.png 1050w, /static/4148092ad044bfb2dbf736a113a6d8ac/8cae4/nyan-cat.png 1109w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/4148092ad044bfb2dbf736a113a6d8ac/8c557/nyan-cat.png" alt="Nyan cat progress bar" title="Nyan cat progress bar" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>If you're a fan of Street Fighter, you can have <a href="https://plugins.jetbrains.com/plugin/12453-hadouken-progress-bar">Hadouken version</a> instead.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 18.857142857142858%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAiUlEQVQI132OQQ7CIBBFuf89PIprY5QhqZgCCRVKYBjgAE7bjQv1JfPzkvmLL1JK606MMYTwWhYpJQBwKlAKQG0O3vujsHV2WMRjmpxzWj/5TbWuhTLWinw/aa313jnF/Xox1mqtZzN7Y0+3fJ5DcNa6LxhjeCmWwivGGKJ9QES5NqR/HDVEZHkDp9zZxPWVwXUAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/9b96bd46a80a7791b7fca6b7186fbce2/c54d4/hadouken.webp 175w, /static/9b96bd46a80a7791b7fca6b7186fbce2/a3432/hadouken.webp 350w, /static/9b96bd46a80a7791b7fca6b7186fbce2/426ac/hadouken.webp 700w, /static/9b96bd46a80a7791b7fca6b7186fbce2/c139f/hadouken.webp 1050w, /static/9b96bd46a80a7791b7fca6b7186fbce2/17ad2/hadouken.webp 1108w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/9b96bd46a80a7791b7fca6b7186fbce2/4edbd/hadouken.png 175w, /static/9b96bd46a80a7791b7fca6b7186fbce2/13ae7/hadouken.png 350w, /static/9b96bd46a80a7791b7fca6b7186fbce2/8c557/hadouken.png 700w, /static/9b96bd46a80a7791b7fca6b7186fbce2/e996b/hadouken.png 1050w, /static/9b96bd46a80a7791b7fca6b7186fbce2/f680b/hadouken.png 1108w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/9b96bd46a80a7791b7fca6b7186fbce2/8c557/hadouken.png" alt="Hadouken progress bar" title="Hadouken progress bar" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="grep-console" style="position:relative;"><a href="#grep-console" aria-label="grep console permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a><a href="https://plugins.jetbrains.com/plugin/7125-grep-console">Grep console</a></h2> <p>This is a powerful plugin with a lot of useful features. It allows you to filter your console output, add custom highlighting or folding of similar line groups. You can even play sound or execute a script when a specific line type occurs in your console output. You can also tail files in your project and much more.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 58.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAB2HAAAdhwGP5fFlAAAB4ElEQVQoz3VSy3abMBDl/7/Fq2YRGzt2SbrtLmlzDFggWQgJ9MA4dmxevcjr3jOMjqS5MxfNBJQdhdIeBlbXusLngcVvtZa04p/Oaesaax3MGMu5CEhGSc5ymh+PR845Y0xKWUqVeTDKikIYkSryWwnWNKfGwzmXpCRI0hSELMsRCrKS0jWnQ2EIIXmOjLyua1XrYyG1sW17bj3AJ1keoGAhSuscJClVcSHKslRVNS9SqqqedTqHK+yVUv6wMtamJA+in9FisVitVrvdbvuy3azXq/XL8yb69fYahuHT04/QY7fdvr1Gz+FmuQqXy2UURTFkk8MhjpP9fu+VQ2me5ezjM84ISdP0/f3jj0ccx4Rkf/cJ8TGMHZNDFuSU5pRBWK3NLEspeFGKQgg8O0xWNfRCM+PzP+G21raUVQoy1+dT46ZpGsexH4bm0nV9Pw7Dveu/bn03jG2tW2dObTsMQ9918PfbDXbIaJDIVldy8uiHkdo72IhAiq7rrveJ0IRmMcYBAThBDfTp+/t7Jhf22p7cgzyMY3UGayZ734/TxHmBzrVf1zk7RI3j5XK5PSo/BE//BxINj4hHmPdQN7cKDdTGzA2t53lEG7H1TcbDGLQUU8DxSemfUGOEXNMY61D5H/mXkAZoGmGqAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/aa3219c7780432ca7dc4876e4c334bb2/c54d4/grep-console.webp 175w, /static/aa3219c7780432ca7dc4876e4c334bb2/a3432/grep-console.webp 350w, /static/aa3219c7780432ca7dc4876e4c334bb2/426ac/grep-console.webp 700w, /static/aa3219c7780432ca7dc4876e4c334bb2/c139f/grep-console.webp 1050w, /static/aa3219c7780432ca7dc4876e4c334bb2/7f403/grep-console.webp 1400w, /static/aa3219c7780432ca7dc4876e4c334bb2/7be94/grep-console.webp 2075w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/aa3219c7780432ca7dc4876e4c334bb2/4edbd/grep-console.png 175w, /static/aa3219c7780432ca7dc4876e4c334bb2/13ae7/grep-console.png 350w, /static/aa3219c7780432ca7dc4876e4c334bb2/8c557/grep-console.png 700w, /static/aa3219c7780432ca7dc4876e4c334bb2/e996b/grep-console.png 1050w, /static/aa3219c7780432ca7dc4876e4c334bb2/2cefc/grep-console.png 1400w, /static/aa3219c7780432ca7dc4876e4c334bb2/ae858/grep-console.png 2075w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/aa3219c7780432ca7dc4876e4c334bb2/8c557/grep-console.png" alt="Grep console plugin" title="Grep console plugin" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="is-something-missing" style="position:relative;"><a href="#is-something-missing" aria-label="is something missing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Is something missing?</h2> <p>What are your favorite IDEA plugins? Share in the comments below.</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[JavaFX Weaver: Integration of JavaFX and Spring Boot]]>https://www.vojtechruzicka.com/javafx-spring-boot/https://www.vojtechruzicka.com/javafx-spring-boot/Mon, 18 Nov 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABAUA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAgP/2gAMAwEAAhADEAAAAbcZswIWXjT/xAAaEAADAQADAAAAAAAAAAAAAAABAgMSACEi/9oACAEBAAEFAtqOVVWuyhjSIyJeCvf/xAAWEQEBAQAAAAAAAAAAAAAAAAABABL/2gAIAQMBAT8BCyX/xAAWEQEBAQAAAAAAAAAAAAAAAAABABL/2gAIAQIBAT8BW03/xAAcEAACAgIDAAAAAAAAAAAAAAABEQACAzEQIUL/2gAIAQEABj8CPeocgu15j1LwWBVnvj//xAAZEAEAAwEBAAAAAAAAAAAAAAABABFhMUH/2gAIAQEAAT8hSW0yAA9hhODYlmFLbnLIlB2EUn//2gAMAwEAAgADAAAAEH8P/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAEDAQE/ECTbW//EABcRAAMBAAAAAAAAAAAAAAAAAAABETH/2gAIAQIBAT8Qm9JH/8QAGxABAAIDAQEAAAAAAAAAAAAAAQARITFhUUH/2gAIAQEAAT8QYALhf1q5d2go4buNB45DGOUMOBR0lpLWC34l61Lsy1ln/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/876e0725a4a93124f754af8ea5915221/c54d4/javafx-spring-boot.webp 175w, /static/876e0725a4a93124f754af8ea5915221/a3432/javafx-spring-boot.webp 350w, /static/876e0725a4a93124f754af8ea5915221/426ac/javafx-spring-boot.webp 700w, /static/876e0725a4a93124f754af8ea5915221/c139f/javafx-spring-boot.webp 1050w, /static/876e0725a4a93124f754af8ea5915221/7f403/javafx-spring-boot.webp 1400w, /static/876e0725a4a93124f754af8ea5915221/fad48/javafx-spring-boot.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/876e0725a4a93124f754af8ea5915221/e52aa/javafx-spring-boot.jpg 175w, /static/876e0725a4a93124f754af8ea5915221/70ebb/javafx-spring-boot.jpg 350w, /static/876e0725a4a93124f754af8ea5915221/29d31/javafx-spring-boot.jpg 700w, /static/876e0725a4a93124f754af8ea5915221/9ecec/javafx-spring-boot.jpg 1050w, /static/876e0725a4a93124f754af8ea5915221/d165a/javafx-spring-boot.jpg 1400w, /static/876e0725a4a93124f754af8ea5915221/b17f8/javafx-spring-boot.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/876e0725a4a93124f754af8ea5915221/29d31/javafx-spring-boot.jpg" alt="Spring Boot and JavaFX integration" title="Spring Boot and JavaFX integration" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li><a href="/javafx-layouts-advanced/">JavaFX Tutorial: Advanced layouts</a></li> <li><a href="/javafx-css/">JavaFX Tutorial: CSS Styling</a></li> <li class="series-current">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</li> </ol> </div> <div class="msg-info">There is <a href="https://github.com/vojtechruz/javafx-weaver-example">an example repository</a> for this blog post, where you can check the final project.</div> <h2 id="javafx--spring" style="position:relative;"><a href="#javafx--spring" aria-label="javafx spring permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaFX &#x26; Spring</h2> <p>When working with Java these days, you rarely work just with plain Java. In most of the projects, people usually use the Spring Framework or Spring Boot, respectively. It has a lot of advantages and many useful features.</p> <p>However, when you're developing JavaFX applications, there is no easy way to integrate it with Spring. The integration does not work out of the box as JavaFX applications have their own lifecycle and take care of instantiating controllers.</p> <h2 id="javafx-weaver" style="position:relative;"><a href="#javafx-weaver" aria-label="javafx weaver permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaFX-Weaver</h2> <p> <a href="https://github.com/rgielen/javafx-weaver/">JavaFX-Weaver</a> is a project by <a href="https://twitter.com/rgielen">Rene Gielen</a>, which aims to integrate Spring and JavaFX. The setup is, fortunately, not that hard.</p> <h2 id="getting-started" style="position:relative;"><a href="#getting-started" aria-label="getting started permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Getting started</h2> <p>Let's try with a plain simple Spring Boot project, and let's try to integrate JavaFX. You can generate a new project using <a href="https://start.spring.io/">Spring Initializr</a>. You don't need to add any dependencies here. If you want to avoid getting into setting up JavaFX as a dependency, select a Java version pre-11 <a href="https://www.vojtechruzicka.com/javafx-getting-started/">as it still contains JavaFXas a part of the JDK</a>.</p> <h3 id="adding-a-controller" style="position:relative;"><a href="#adding-a-controller" aria-label="adding a controller permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding a controller</h3> <p>We'll need a controller and a companion <code class="language-text">.fxml</code> view to be able to test that our application works properly both with Spring and JavaFX. Let' create a controller first. Let's keep it empty for now.</p> <div class="gatsby-code-button-container" data-toaster-id="90262813597148330000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public class MyController { }`, `90262813597148330000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyController</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></code></pre></div> <h3 id="adding-a-view" style="position:relative;"><a href="#adding-a-view" aria-label="adding a view permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding a view</h3> <p>Now we need a <code class="language-text">.fxml</code> file, which will be used with our controller as a view. We'll place it in the <code class="language-text">resources</code> folder. <strong>For the integration to work properly, it is necessary to create it in the resources folder, but in a directory structure matching the package where is our controller</strong>.</p> <p>For example, let's assume our controller is in package <code class="language-text">com.vojtechruzicka.javafxweaverexample</code>. The <code class="language-text">.fxml</code> file needs to be placed precisely here:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">src\main\resources\com\vojtechruzicka\javafxweaverexample</code></pre></div> <p>Let's call our file <code class="language-text">main-scene.fxml</code>.</p> <h2 id="dependencies" style="position:relative;"><a href="#dependencies" aria-label="dependencies permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dependencies</h2> <p>If you are using plain Spring, the setup is little different, but for Spring Boot you just need to add the following dependency to your <code class="language-text">pom.xml</code> file:</p> <div class="gatsby-code-button-container" data-toaster-id="64856495312946880000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<dependency> <groupId>net.rgielen</groupId> <artifactId>javafx-weaver-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency>`, `64856495312946880000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>net.rgielen<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>javafx-weaver-spring-boot-starter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>1.3.0<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span></code></pre></div> <p>Or this one if you are using Gradle:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">implementation &#39;javafx-weaver-spring-boot-starter:1.3.0&#39;</code></pre></div> <h2 id="spring-boot-application-class" style="position:relative;"><a href="#spring-boot-application-class" aria-label="spring boot application class permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Spring Boot Application class</h2> <p>When we generated our base Spring Boot application, the main application class was also generated for us. It is the class annotated with <code class="language-text">@SpringBootApplication</code>, which is used as an entry point to run the whole app.</p> <p>But wait! JavaFX also has its <a href="https://www.vojtechruzicka.com/javafx-hello-world/#application-class">main application class</a>, which is used as an entry point for starting JavaFX applications.</p> <p>That's confusing. So which one should be actually used to run our app, which is both Spring Boot and JavaFX?</p> <p>We'll still use our <code class="language-text">@SpringBootApplication</code> with a slight modification. Instead of running the Spring app directly, we'll use it to run our JavaFX app. And the JavaFX Application will be responsible for properly starting the Spring application context and integrating everything together using JavaFX Weaver.</p> <p>We need first to make sure that the Spring Boot app launches our JavaFX app.</p> <div class="gatsby-code-button-container" data-toaster-id="42874474169304680000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`import javafx.application.Application; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootExampleApplication { public static void main(String[] args) { // This is how normal Spring Boot app would be launched // SpringApplication.run(SpringBootExampleApplication.class, args); // JavaFxApplication doesn't exist yet, // we'll create it in the next step Application.launch(JavaFxApplication.class, args); } }`, `42874474169304680000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">javafx<span class="token punctuation">.</span>application<span class="token punctuation">.</span></span><span class="token class-name">Application</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>boot<span class="token punctuation">.</span>autoconfigure<span class="token punctuation">.</span></span><span class="token class-name">SpringBootApplication</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@SpringBootApplication</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SpringBootExampleApplication</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// This is how normal Spring Boot app would be launched</span> <span class="token comment">// SpringApplication.run(SpringBootExampleApplication.class, args);</span> <span class="token comment">// JavaFxApplication doesn't exist yet, </span> <span class="token comment">// we'll create it in the next step</span> <span class="gatsby-highlight-code-line"> <span class="token class-name">Application</span><span class="token punctuation">.</span><span class="token function">launch</span><span class="token punctuation">(</span><span class="token class-name">JavaFxApplication</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span></span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <h2 id="javafx-application-class" style="position:relative;"><a href="#javafx-application-class" aria-label="javafx application class permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JavaFX Application class</h2> <p>Now our <code class="language-text">@SpringBootApplication</code> is calling <code class="language-text">JavaFxApplication</code>, which does not exist yet. Let's create it now.</p> <div class="gatsby-code-button-container" data-toaster-id="727083829229635600" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public class JavaFxApplication extends Application { private ConfigurableApplicationContext applicationContext; @Override public void init() { String[] args = getParameters().getRaw().toArray(new String[0]); this.applicationContext = new SpringApplicationBuilder() .sources(SpringBootExampleApplication.class) .run(args); } }`, `727083829229635600`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JavaFxApplication</span> <span class="token keyword">extends</span> <span class="token class-name">Application</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">ConfigurableApplicationContext</span> applicationContext<span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args <span class="token operator">=</span> <span class="token function">getParameters</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getRaw</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toArray</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationContext <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SpringApplicationBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">sources</span><span class="token punctuation">(</span><span class="token class-name">SpringBootExampleApplication</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">run</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>When the JavaFX initializes, it creates a new application context based on the configuration in our <code class="language-text">SpringBootExampleApplication</code> - the main class of the Spring Boot application, which we modified in the previous step.</p> <p>Now we have a Spring Boot application running with our new application context. But we need to make sure the context is properly closed when the JavaFX application terminates. For example, when you close the window. Let's handle this now.</p> <div class="gatsby-code-button-container" data-toaster-id="37685122626439950000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@Override public void stop() { this.applicationContext.close(); Platform.exit(); }`, `37685122626439950000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationContext<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Platform</span><span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Now the last part is to actually create a new window (Stage) and show it.</p> <div class="gatsby-code-button-container" data-toaster-id="36949491198201364000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@Override public void start(Stage stage) { FxWeaver fxWeaver = applicationContext.getBean(FxWeaver.class); Parent root = fxWeaver.loadView(MyController.class); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }`, `36949491198201364000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token class-name">Stage</span> stage<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">FxWeaver</span> fxWeaver <span class="token operator">=</span> applicationContext<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span><span class="token class-name">FxWeaver</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Parent</span> root <span class="token operator">=</span> fxWeaver<span class="token punctuation">.</span><span class="token function">loadView</span><span class="token punctuation">(</span><span class="token class-name">MyController</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Scene</span> scene <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Scene</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">;</span> stage<span class="token punctuation">.</span><span class="token function">setScene</span><span class="token punctuation">(</span>scene<span class="token punctuation">)</span><span class="token punctuation">;</span> stage<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>This is where Fx Weaver comes into play. We need to obtain its bean from the application context and then use it to load our FXML. </p> <h2 id="spring-managed-controllers" style="position:relative;"><a href="#spring-managed-controllers" aria-label="spring managed controllers permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Spring-managed controllers</h2> <p>Traditionally we would create our Stage using <code class="language-text">FXMLLoader</code>, which would load the FXML file and create a Controller instance declared in it for us.</p> <div class="gatsby-code-button-container" data-toaster-id="31019546795650556000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource(&quot;/main-scene.fxml&quot;); loader.setLocation(xmlUrl); Parent root = loader.load(); Scene scene = new Scene(root); stage.setScene(scene); stage.show();`, `31019546795650556000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">FXMLLoader</span> loader <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FXMLLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">URL</span> xmlUrl <span class="token operator">=</span> <span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResource</span><span class="token punctuation">(</span><span class="token string">"/main-scene.fxml"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> loader<span class="token punctuation">.</span><span class="token function">setLocation</span><span class="token punctuation">(</span>xmlUrl<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Parent</span> root <span class="token operator">=</span> loader<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Scene</span> scene <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Scene</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span><span class="token punctuation">;</span> stage<span class="token punctuation">.</span><span class="token function">setScene</span><span class="token punctuation">(</span>scene<span class="token punctuation">)</span><span class="token punctuation">;</span> stage<span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>So why are we using FX Weaver instead? What's problematic is that <code class="language-text">FXMLLoader</code> creates the controller instance for us. That means it is not created and managed by Spring. Therefore we cannot use dependency injection and other Spring goodies in our controllers. And that's why we introduced Spring in our JavaFX in the first place!</p> <p>But when FX Weaver creates the controller for us, it creates it as a Spring-managed bean, so we can fully utilize the features of Spring.</p> <h2 id="enabling-spring-for-the-controller" style="position:relative;"><a href="#enabling-spring-for-the-controller" aria-label="enabling spring for the controller permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Enabling Spring for the controller</h2> <p>The first thing we need to do is to annotate our existing JavaFX controller with <code class="language-text">@Component</code> so it gets recognized and managed by Spring. Next, we need to add <code class="language-text">@FxmlView</code> annotation, so it gets recognized by FX Weaver.</p> <div class="gatsby-code-button-container" data-toaster-id="12562506180879086000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`import net.rgielen.fxweaver.core.FxmlView; import org.springframework.stereotype.Component; @Component @FxmlView(&quot;main-stage.fxml&quot;) public class MyController { }`, `12562506180879086000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">net<span class="token punctuation">.</span>rgielen<span class="token punctuation">.</span>fxweaver<span class="token punctuation">.</span>core<span class="token punctuation">.</span></span><span class="token class-name">FxmlView</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Component</span><span class="token punctuation">;</span> <span class="gatsby-highlight-code-line"><span class="token annotation punctuation">@Component</span></span><span class="gatsby-highlight-code-line"><span class="token annotation punctuation">@FxmlView</span><span class="token punctuation">(</span><span class="token string">"main-stage.fxml"</span><span class="token punctuation">)</span></span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyController</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span></code></pre></div> <p>Note the parameter of <code class="language-text">@FxmlView(&quot;main-stage.fxml&quot;)</code>. It specifies the name of your <code class="language-text">.fxml</code> file, which should be matched with the controller. It is optional. If you don't specify it, Fx Weaver will use the name of the controller class as the file name with <code class="language-text">.fxml</code> extension. <strong>The FXML file needs to be in the same package as the controller, but in the resources folder</strong>.</p> <h2 id="making-sure-everything-works" style="position:relative;"><a href="#making-sure-everything-works" aria-label="making sure everything works permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Making sure everything works</h2> <p>Now let's make sure everything works and integrates nicely. Let's run our <code class="language-text">@SpringBootApplication</code> with its <code class="language-text">main</code> method. You should see a simple window with a label, nothing fancy.</p> <p>Ok, that means that the application runs, but we didn't really do anything Spring-specific in our controller. No dependency injection or anything. Let's try that now.</p> <h3 id="adding-a-service" style="position:relative;"><a href="#adding-a-service" aria-label="adding a service permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Adding a Service</h3> <p>To make sure Spring integration works properly, let's create a new Spring-managed service. Later, we'll inject it in our controller and use it there.</p> <div class="gatsby-code-button-container" data-toaster-id="80861937729166200000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`import org.springframework.stereotype.Service; @Service public class WeatherService { public String getWeatherForecast() { return &quot;It's gonna snow a lot. Brace yourselves, the winter is coming.&quot;; } }`, `80861937729166200000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>stereotype<span class="token punctuation">.</span></span><span class="token class-name">Service</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@Service</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">WeatherService</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getWeatherForecast</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"It's gonna snow a lot. Brace yourselves, the winter is coming."</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>Nothing special, it's a service for weather forecasting, which is not very dynamic right now, but it will be enough for our example.</p> <h3 id="injecting-the-service" style="position:relative;"><a href="#injecting-the-service" aria-label="injecting the service permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Injecting the service</h3> <p>Now let's inject our new service into our existing controller. It's the usual Spring stuff, nothing special here.</p> <div class="gatsby-code-button-container" data-toaster-id="29439075215944065000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@Component @FxmlView(&quot;main-stage.fxml&quot;) public class MyController { private WeatherService weatherService; @Autowired public MyController(WeatherService weatherService) { this.weatherService = weatherService; } }`, `29439075215944065000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@Component</span> <span class="token annotation punctuation">@FxmlView</span><span class="token punctuation">(</span><span class="token string">"main-stage.fxml"</span><span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyController</span> <span class="token punctuation">{</span> <span class="gatsby-highlight-code-line"> <span class="token keyword">private</span> <span class="token class-name">WeatherService</span> weatherService<span class="token punctuation">;</span></span> <span class="gatsby-highlight-code-line"> <span class="token annotation punctuation">@Autowired</span></span><span class="gatsby-highlight-code-line"> <span class="token keyword">public</span> <span class="token class-name">MyController</span><span class="token punctuation">(</span><span class="token class-name">WeatherService</span> weatherService<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><span class="gatsby-highlight-code-line"> <span class="token keyword">this</span><span class="token punctuation">.</span>weatherService <span class="token operator">=</span> weatherService<span class="token punctuation">;</span></span><span class="gatsby-highlight-code-line"> <span class="token punctuation">}</span></span><span class="token punctuation">}</span></code></pre></div> <h3 id="loading-the-forecast" style="position:relative;"><a href="#loading-the-forecast" aria-label="loading the forecast permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Loading the forecast</h3> <p>Now we need to load the data from our service somehow. Let's change our FMXL view, so:</p> <ol> <li>There is a button which loads the data from <code class="language-text">WeatherService</code> on click</li> <li>The loaded data is shown in a label</li> </ol> <div class="gatsby-code-button-container" data-toaster-id="79086545838474500000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <VBox xmlns=&quot;http://javafx.com/javafx&quot; xmlns:fx=&quot;http://javafx.com/fxml&quot; fx:controller=&quot;com.vojtechruzicka.javafxweaverexample.MyController&quot; prefHeight=&quot;100.0&quot; prefWidth=&quot;350.0&quot; spacing=&quot;10&quot; alignment=&quot;CENTER&quot;> <Label fx:id=&quot;weatherLabel&quot;/> <Button onAction=&quot;#loadWeatherForecast&quot;>Get weather</Button> </VBox>`, `79086545838474500000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token prolog">&lt;?xml version="1.0" encoding="UTF-8"?></span> <span class="token prolog">&lt;?import javafx.scene.control.*?></span> <span class="token prolog">&lt;?import javafx.scene.layout.*?></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>VBox</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://javafx.com/javafx<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>fx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://javafx.com/fxml<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">fx:</span>controller</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.vojtechruzicka.javafxweaverexample.MyController<span class="token punctuation">"</span></span> <span class="token attr-name">prefHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100.0<span class="token punctuation">"</span></span> <span class="token attr-name">prefWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>350.0<span class="token punctuation">"</span></span> <span class="token attr-name">spacing</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">alignment</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>CENTER<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name"><span class="token namespace">fx:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>weatherLabel<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span> <span class="token attr-name">onAction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#loadWeatherForecast<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Get weather<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>VBox</span><span class="token punctuation">></span></span></code></pre></div> <p>Note the <code class="language-text">fx:id=&quot;weatherLabel&quot;</code> identifier, we'll use it to get access to this label in our controller so that we can change its text.</p> <p><code class="language-text">onAction=&quot;#loadWeatherForecast&quot;</code> is a method on our controller, which should be called when the button is clicked. We still need to add it to the controller. Let's do it now.</p> <h3 id="controller-logic" style="position:relative;"><a href="#controller-logic" aria-label="controller logic permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Controller logic</h3> <p>The last step is to change our controller, so it reacts to the button click in the view, loads weather forecast data, and sets it to our label.</p> <p>So we need a reference to the label from our view so that we can change its text. We need to select its name to match the <code class="language-text">fx:id=&quot;weatherLabel&quot;</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="99280625616471690000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@FXML private Label weatherLabel;`, `99280625616471690000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@FXML</span> <span class="token keyword">private</span> <span class="token class-name">Label</span> weatherLabel<span class="token punctuation">;</span></code></pre></div> <p>Now we need to add the method, which is called when the button is clicked - <code class="language-text">onAction=&quot;#loadWeatherForecast&quot;</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="72454481968370120000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public void loadWeatherForecast(ActionEvent actionEvent) { this.weatherLabel.setText(weatherService.getWeatherForecast()); }`, `72454481968370120000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">loadWeatherForecast</span><span class="token punctuation">(</span><span class="token class-name">ActionEvent</span> actionEvent<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>weatherLabel<span class="token punctuation">.</span><span class="token function">setText</span><span class="token punctuation">(</span>weatherService<span class="token punctuation">.</span><span class="token function">getWeatherForecast</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>In this method, we take the weather forecast from the service and set it to our label, which we defined before. </p> <p>If you run the app now, after you click the button, it should load the current weather forecast.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 37.14285714285714%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAA4UlEQVQY031Qy27DIBD0//9TDjn0GiWNopwajIFgReKxa4xb2a46xklbVWrmMDuwr4GqS+mtUSml/n+kAgjnHETXdSEEcEXM4mo+n2KapnEcIdA5zzN0zpmZKwzIqc8FmF2obLvHvIo1OwzDyuj03lcY1ra2KVBAo7TWCgcprb3q5aYxWksp67pGyhgjhLC2jTEsm2OMRHRnIia6hbgX+lTr40W9CmNc4EcBlVIfwrIZzfDwbRsG33N/o7TZn18Op+3uuD2cpeMPGH486cc2Av5w3fwbnshF8sQ+/k2h2BV8AbJ0iVxmFnAjAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/38f323dcb778c39807587960447c8f6c/c54d4/weather-app.webp 175w, /static/38f323dcb778c39807587960447c8f6c/a3432/weather-app.webp 350w, /static/38f323dcb778c39807587960447c8f6c/426ac/weather-app.webp 700w, /static/38f323dcb778c39807587960447c8f6c/30833/weather-app.webp 946w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/38f323dcb778c39807587960447c8f6c/4edbd/weather-app.png 175w, /static/38f323dcb778c39807587960447c8f6c/13ae7/weather-app.png 350w, /static/38f323dcb778c39807587960447c8f6c/8c557/weather-app.png 700w, /static/38f323dcb778c39807587960447c8f6c/36c33/weather-app.png 946w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/38f323dcb778c39807587960447c8f6c/8c557/weather-app.png" alt="Weather app is running!" title="Weather app is running!" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="accessing-components-from-view" style="position:relative;"><a href="#accessing-components-from-view" aria-label="accessing components from view permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Accessing components from view</h2> <p>Same as in plain JavaFX, you can declare components from view to be injected to your controller, so you can interact with them.</p> <div class="gatsby-code-button-container" data-toaster-id="27039093122119760000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`@FXML private Label weatherLabel;`, `27039093122119760000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token annotation punctuation">@FXML</span> <span class="token keyword">private</span> <span class="token class-name">Label</span> weatherLabel<span class="token punctuation">;</span></code></pre></div> <p>We already saw this works well. You just need to be careful about timing. Our controller is annotated by <code class="language-text">@Component</code>, so it is a regular Spring-managed bean. It means it is instantiated by Spring when the application context starts and all the dependencies are injected. However, the weaving by FX Weaver happens later. And during this weaving, the component references are injected. </p> <p>This has one implication. In your constructor and <code class="language-text">@PostConstruct</code> you can already work with Spring injected dependencies as usual. However, be aware that during this time, references to components from the view are not yet available and are therefore null.</p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>JavaFX Weaver provides a nice and easy way to integrate Spring with JavaFX applications. It is otherwise not so straightforward as JavaFX manages its own lifecycle and lifecycle of its controllers. JavaFX Weaver makes the integration possible and quite straightforward, so you can finally use all the cool features of Spring even with JavaFX.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li><a href="/javafx-layouts-advanced/">JavaFX Tutorial: Advanced layouts</a></li> <li><a href="/javafx-css/">JavaFX Tutorial: CSS Styling</a></li> <li class="series-current">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</li> </ol> </div>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Commitlint: validate commit conventions automatically]]>https://www.vojtechruzicka.com/commitlint/https://www.vojtechruzicka.com/commitlint/Mon, 11 Nov 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAgP/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB4vxtjVEyX//EABkQAQEBAAMAAAAAAAAAAAAAAAIAAQMSIf/aAAgBAQABBQIIw5DJew3rl//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AVf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwFH/8QAFxAAAwEAAAAAAAAAAAAAAAAAARAgMf/aAAgBAQAGPwLIC//EABwQAQACAgMBAAAAAAAAAAAAAAEAMREhQVFx0f/aAAgBAQABPyEGNq7jKK1GfmZ0+RlDcyvM/9oADAMBAAIAAwAAABBUP//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EFv/xAAWEQADAAAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8QapJ//8QAGxABAQACAwEAAAAAAAAAAAAAAREAITFBUXH/2gAIAQEAAT8QSuxC9n3LPYrd3LcQKzXHDBH3Y9atpjvL3n//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/a2c3f428306d4dfce89c831685f075c6/c54d4/commitlint.webp 175w, /static/a2c3f428306d4dfce89c831685f075c6/a3432/commitlint.webp 350w, /static/a2c3f428306d4dfce89c831685f075c6/426ac/commitlint.webp 700w, /static/a2c3f428306d4dfce89c831685f075c6/c139f/commitlint.webp 1050w, /static/a2c3f428306d4dfce89c831685f075c6/7f403/commitlint.webp 1400w, /static/a2c3f428306d4dfce89c831685f075c6/fad48/commitlint.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/a2c3f428306d4dfce89c831685f075c6/e52aa/commitlint.jpg 175w, /static/a2c3f428306d4dfce89c831685f075c6/70ebb/commitlint.jpg 350w, /static/a2c3f428306d4dfce89c831685f075c6/29d31/commitlint.jpg 700w, /static/a2c3f428306d4dfce89c831685f075c6/9ecec/commitlint.jpg 1050w, /static/a2c3f428306d4dfce89c831685f075c6/d165a/commitlint.jpg 1400w, /static/a2c3f428306d4dfce89c831685f075c6/b17f8/commitlint.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/a2c3f428306d4dfce89c831685f075c6/29d31/commitlint.jpg" alt="Commitlint" title="Commitlint" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Automatically check and enforce your commit conventions either with various available configurations.</p> <h2 id="commit-conventions" style="position:relative;"><a href="#commit-conventions" aria-label="commit conventions permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commit conventions</h2> <p>Although you can put pretty much anything in your commit messages, it is beneficial to have a more standardized and structured approach. It is better to stick to a convention, so all your commit messages follow the same structure. This has many advantages:</p> <ul> <li>Your commit history is more readable and easier to navigate</li> <li>You can automatically generate changelogs</li> <li> <p>Based on the type of changes, you can properly bump your version when using <a href="https://semver.org/">semantic versioning</a>: </p> <ul> <li>major: when introducing breaking changes </li> <li>minor: when adding a feature</li> <li>fix: when adding a fix</li> </ul> </li> </ul> <p>There are various conventions. One example can be <a href="https://www.conventionalcommits.org/">Conventional Commits</a>.</p> <p>Conventions can be very useful, but only as long as you can make sure everybody follows them. You can rely on your developers' discipline, but it is risky. Especially with external contributors to your codebase who may not be familiar enough with your convention. What's much better is having an automated tool to check your commits for you and reject them if they don't follow your conventions.</p> <h2 id="githooks--husky" style="position:relative;"><a href="#githooks--husky" aria-label="githooks husky permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Githooks &#x26; Husky</h2> <p>Fortunately, Git already allows you to perform some actions when specific events are triggered. It is called git hooks. You can react in many phases of the git workflow, such as:</p> <ul> <li>pre-commit</li> <li>pre-push</li> <li>pre-rebase</li> <li>post-update</li> </ul> <p>The bad news is that this is configured locally in your <code class="language-text">.git</code> directory, so by default, each developer needs to be sure to install their hooks. If they don't, there are no checks, and you cannot check your conventions.</p> <p>Fortunately, there is a tool called Husky for Node/NPM projects, which solves this issue. You can define your hooks in your project and they are automatically installed for you when running the project. This way all the developers will have proper hooks installed with no effort. If you need to change your hooks, you can do it n just one place, and it gets propagated to all your developers. You can read more about Husky in one of my previous posts:</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/githooks-husky/" style="box-shadow: none;">Easy git hooks with Husky</a></h4><small class="front-post-info"><span class="front-post-info-date">21 June, 2018</span><div class="post-tags"><ul><li><a href="/tags/git/">#Git</a></li></ul></div></small><div><a class="front-post-image" href="/githooks-husky/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 66.6875%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAMBAgQF/8QAFQEBAQAAAAAAAAAAAAAAAAAAAQD/2gAMAwEAAhADEAAAAU7781JGhf/EABsQAAICAwEAAAAAAAAAAAAAAAECABEDBBMy/9oACAEBAAEFAhGvKgcNG8pY2Oaz/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8BV//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/AUf/xAAbEAACAgMBAAAAAAAAAAAAAAAAAQIREBIhUf/aAAgBAQAGPwIWjpr07wZKpY//xAAbEAEBAQACAwAAAAAAAAAAAAABEQAhMVFhgf/aAAgBAQABPyFgr9cYpooIoYlzkAGeNywsr7xyM7bv/9oADAMBAAIAAwAAABDz/wD/xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQMBAT8QHY//xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8QTI//xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAhMVFxQf/aAAgBAQABPxCGeJTzISADGrpTx79wAFQigmUXFDDHJwaudiezHOlaAPuf/9k=" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition-delay: 500ms;"><picture><source srcset="/linked/husky/5e4a3/husky.jpg 45w, /linked/husky/e451c/husky.jpg 90w, /linked/husky/29fd0/husky.jpg 180w, /linked/husky/b3ebb/husky.jpg 270w, /linked/husky/8841e/husky.jpg 360w, /linked/husky/989b1/husky.jpg 1600w" sizes="(max-width: 180px) 100vw, 180px"><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/husky/5e4a3/husky.jpg 45w, /linked/husky/e451c/husky.jpg 90w, /linked/husky/29fd0/husky.jpg 180w, /linked/husky/b3ebb/husky.jpg 270w, /linked/husky/8841e/husky.jpg 360w, /linked/husky/989b1/husky.jpg 1600w" src="/linked/husky/29fd0/husky.jpg" alt="" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 500ms ease 0s;"></picture><noscript><picture><source srcset="/linked/husky/5e4a3/husky.jpg 45w, /linked/husky/e451c/husky.jpg 90w, /linked/husky/29fd0/husky.jpg 180w, /linked/husky/b3ebb/husky.jpg 270w, /linked/husky/8841e/husky.jpg 360w, /linked/husky/989b1/husky.jpg 1600w" sizes="(max-width: 180px) 100vw, 180px" /><img loading="lazy" sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/husky/5e4a3/husky.jpg 45w, /linked/husky/e451c/husky.jpg 90w, /linked/husky/29fd0/husky.jpg 180w, /linked/husky/b3ebb/husky.jpg 270w, /linked/husky/8841e/husky.jpg 360w, /linked/husky/989b1/husky.jpg 1600w" src="/linked/husky/29fd0/husky.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">Effortless creation and management of git hooks for your Node/NPM projects with Husky.</span></div></div> <p>While Husky can be used to enforce your commit conventions, it can do much more. You can run your static code analysis, tests, automatic code formatting, and much more when committing.</p> <h2 id="commitlint" style="position:relative;"><a href="#commitlint" aria-label="commitlint permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commitlint</h2> <p>Commitlint is an automated tool that can check your commit conventions for you and reject the commit if it does not follow your rules. These rules, of course, can be configured. Under the hood, Commitlint uses Husky git hooks.</p> <h2 id="local-setup" style="position:relative;"><a href="#local-setup" aria-label="local setup permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Local setup</h2> <h3 id="husky" style="position:relative;"><a href="#husky" aria-label="husky permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Husky</h3> <p>Since Commitlint utilizes Husky, we need to install it first. Husky is provided as dev-dependency, so it is used only locally and not bundled with your production code.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">npm install --save-dev husky</code></pre></div> <p>Now we need to register a hook for checking a commit message. The hook is called <code class="language-text">commit-msg</code> and can be registered in your <code class="language-text">package.json</code>.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">{ &quot;husky&quot;: { &quot;hooks&quot;: { &quot;commit-msg&quot;: &quot;commitlint -E HUSKY_GIT_PARAMS&quot; } } }</code></pre></div> <p>Alternatively, you can use a dedicated <code class="language-text">.huskyrc</code> file, which contains only Husky configuration:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">{ &quot;hooks&quot;: { &quot;commit-msg&quot;: &quot;commitlint -E HUSKY_GIT_PARAMS&quot; } }</code></pre></div> <h3 id="commitlint-1" style="position:relative;"><a href="#commitlint-1" aria-label="commitlint 1 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commitlint</h3> <p>Now when We have Husky, we need to install Commitlint CLI, which will be executed by Husky hook to validate commit messages. Again, it is just a dev dependency, which is not included in your production bundle.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">npm install --save-dev @commitlint/cli</code></pre></div> <h3 id="convention-configuration" style="position:relative;"><a href="#convention-configuration" aria-label="convention configuration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Convention configuration</h3> <p>Commitlint is just a tool for checking if your messages follow your conventions. It does not for you to use a specific convention, you can use whatever you want, it is fully configurable. It offers some configurations out of Currently supported configurations are:</p> <ul> <li><a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional">@commitlint/config-conventional</a></li> <li><a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-lerna-scopes">@commitlint/config-lerna-scopes</a></li> <li><a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-patternplate">@commitlint/config-patternplate</a></li> <li><a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-angular">@commitlint/config-angular</a></li> <li><a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-angular-type-enum">@commitlint/config-angular-type-enum</a></li> </ul> <p>Let's use <code class="language-text">@commitlint/config-conventional</code> for now, but the process would be the same for any different configuration. We just need to install the proper package:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">npm install --save-dev @commitlint/config-conventional</code></pre></div> <p>The last step is to create a Commitlint configuration file, where you define, which configuration should be used. We'll set up the one we just installed. Create a new file called <code class="language-text">commitlint.config.js</code> in the root directory.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">module.exports = { extends: [&#39;@commitlint/config-conventional&#39;] };</code></pre></div> <h3 id="verifying-the-setup" style="position:relative;"><a href="#verifying-the-setup" aria-label="verifying the setup permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Verifying the setup</h3> <p>Now we're good to go. Let's just make sure everything works. Let's make sure our commit messages are tested against the <a href="https://www.conventionalcommits.org/">Conventional Commits</a> specification. Let's try an ordinary, not structured commit message first.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">C:\projects\commitlint-example&gt;git commit -m &quot;created base project structure&quot; husky &gt; commit-msg (node v10.16.3) ⧗ input: created base project structure ✖ subject may not be empty [subject-empty] ✖ type may not be empty [type-empty] ✖ found 2 problems, 0 warnings ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint husky &gt; commit-msg hook failed (add --no-verify to bypass)</code></pre></div> <p>The commit was rejected due to missing subject and type. Let's fix that.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">git commit -m &quot;feat: created base project structure</code></pre></div> <p>Now the commit was accepted. We added the type of the commit <code class="language-text">feat</code> and the part after the colon <code class="language-text">:</code> is considered the subject.</p> <p>The full structure of conventional commits is the following:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">&lt;type&gt;[optional scope]: &lt;description&gt; [optional body] [optional footer(s)]</code></pre></div> <h2 id="continous-integration" style="position:relative;"><a href="#continous-integration" aria-label="continous integration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Continous Integration</h2> <p>Local setup, as described above, is necessary and useful as the first line of defense. However, it is not bulletproof. Developers can Tinker with the local setup or suppress git hooks. You cannot rely purely on the local Husky setup. As another line of defense, it is viable to integrate Commitlint with your CI. You can reject invalid commits on the server even if developers manage to sneak them in.</p> <h3 id="travis" style="position:relative;"><a href="#travis" aria-label="travis permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Travis</h3> <p>Commitlint supports integration with <a href="https://travis-ci.org/">Travis CI</a>. First, elt's create a <code class="language-text">.travis.yml</code> file in your root directory. We need to configure that it should run the Commitlint script.</p> <div class="gatsby-code-button-container" data-toaster-id="50728118767303720000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`language: node_js node_js: - node script: - commitlint-travis`, `50728118767303720000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="yaml"><pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">language</span><span class="token punctuation">:</span> node_js <span class="token key atrule">node_js</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> node <span class="token key atrule">script</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> commitlint<span class="token punctuation">-</span>travis</code></pre></div> <p>Now we need to install Travis support in our project:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">npm install --save-dev @commitlint/travis-cli</code></pre></div> <p>Now Travis can watch your Pull Requests and check whether new commits are compliant.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 51.42857142857144%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABbklEQVQoz3VSSW7kMAzs/38jt5zmDznkA0GAAXLJJG23LZnULlKbQ7eTwVyGKBQkwkUWSr4QMZdCYxCXzJyIErN13gm8zzkjGjQGEQHQ+7D/U5eHx19vv9/i03O6rQLWGy9rJ97/U713GxuGSqVdNjTJh/jympwL1pac91pH7/LdkLrzKRvjbOy1DUHv4yKX1jvVqtZZAYKxagOlQQM4H5yPwsaK31Rq4ztqbSaUmOtd3HpyeXp/v07LvGyf0+06r/OiFwUuJDBeJmow6UhH3FZhZdmlcopLgheKwOQ5W86BsqfshJlCoSgOZUEf38YFtXXpHOLeCttXUH/0+qFBIS4WJmduFmfUnxbmTCyxx5hOFvRj1H4XHzP3EClT7UOMyJ4jj37ysVaqt5+qx60LTtstKJ3UJoh6ixvEDYPaAlivIPqYS8skf0H9CwlP8J22WRR+THidzaqtj/J2xgWJ1ARGX9Czk46cQxHYWE7xF1lWP+BIbHV2AAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/ae9431ccf95f08ff83c1caeed8467d73/c54d4/travis.webp 175w, /static/ae9431ccf95f08ff83c1caeed8467d73/a3432/travis.webp 350w, /static/ae9431ccf95f08ff83c1caeed8467d73/426ac/travis.webp 700w, /static/ae9431ccf95f08ff83c1caeed8467d73/c139f/travis.webp 1050w, /static/ae9431ccf95f08ff83c1caeed8467d73/7f403/travis.webp 1400w, /static/ae9431ccf95f08ff83c1caeed8467d73/06e3f/travis.webp 1873w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/ae9431ccf95f08ff83c1caeed8467d73/4edbd/travis.png 175w, /static/ae9431ccf95f08ff83c1caeed8467d73/13ae7/travis.png 350w, /static/ae9431ccf95f08ff83c1caeed8467d73/8c557/travis.png 700w, /static/ae9431ccf95f08ff83c1caeed8467d73/e996b/travis.png 1050w, /static/ae9431ccf95f08ff83c1caeed8467d73/2cefc/travis.png 1400w, /static/ae9431ccf95f08ff83c1caeed8467d73/c0786/travis.png 1873w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/ae9431ccf95f08ff83c1caeed8467d73/8c557/travis.png" alt="Travis integration" title="Travis integration" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h3 id="commitlint-github-action" style="position:relative;"><a href="#commitlint-github-action" aria-label="commitlint github action permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commitlint GitHub action</h3> <p>Even simpler alternative to Travis is adding a <a href="https://github.com/features/actions">GitHub Action</a>. <a href="https://probot.github.io/apps/commitlint/">There is an action for Commitlint available</a>.</p> <p>The setup is super-easy. You just need to go to [this page] and click <code class="language-text">Install</code>. You can select whether this action should apply for all your repost or just selected ones. And that's it. Now, whenever you create a Pull Request, it will be automatically checked by Commitlint.</p> <p>Now if your PR contains invalid commit messages, the checks will fail:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 46.857142857142854%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABVElEQVQoz3VRy05jMQzt/38NKzZskJDYDUKqxA5p5rZTWu7Nw3nZcV44FCE2HB1FthKfYzu7MUYphVqjzAkJSZiNBQAXY0JEa8E5J6e1VvLxA7ub27vD30Pcv2QfOCYOkZynhDwhsvUa8FdeW+sRS6LKpe+e9i/q8u7uH4KcpzfWphnTmcfvKH0kbrNYElHj1qS386o1hIAZfAQXnI8+JK5dHIWZufX273j687yX/mttu95HLYx2oQSUPEbIGAg9oqcZBOYkrU52wfAhXtZNJhDL6dwqpfVxO7+q92VTZ6NOoA5OHUEdzbqY7UCUxUr2J8iyWGlBxPr4LP5UDYkwlyZT9EkJWr/GvdQqFLO5v8lp267OcpOMJQtoIFlI2qCxqA0Zm5SmGEtr8vwq8S0knMVyZ98uejma5b89rwBePtkYUEAacANSLsv+jCfts9AGzjyLPwCOnAaT+K83CgAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/1db4ec1fb73c2779570089760f3a33ec/c54d4/commitlint-github-action.webp 175w, /static/1db4ec1fb73c2779570089760f3a33ec/a3432/commitlint-github-action.webp 350w, /static/1db4ec1fb73c2779570089760f3a33ec/426ac/commitlint-github-action.webp 700w, /static/1db4ec1fb73c2779570089760f3a33ec/c139f/commitlint-github-action.webp 1050w, /static/1db4ec1fb73c2779570089760f3a33ec/7f403/commitlint-github-action.webp 1400w, /static/1db4ec1fb73c2779570089760f3a33ec/c1b6d/commitlint-github-action.webp 1865w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/1db4ec1fb73c2779570089760f3a33ec/4edbd/commitlint-github-action.png 175w, /static/1db4ec1fb73c2779570089760f3a33ec/13ae7/commitlint-github-action.png 350w, /static/1db4ec1fb73c2779570089760f3a33ec/8c557/commitlint-github-action.png 700w, /static/1db4ec1fb73c2779570089760f3a33ec/e996b/commitlint-github-action.png 1050w, /static/1db4ec1fb73c2779570089760f3a33ec/2cefc/commitlint-github-action.png 1400w, /static/1db4ec1fb73c2779570089760f3a33ec/3955b/commitlint-github-action.png 1865w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/1db4ec1fb73c2779570089760f3a33ec/8c557/commitlint-github-action.png" alt="Github checks fail" title="Github checks fail" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>And you will get a comment explaining all the failures:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 45.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/klEQVQoz4WRi24EIQhF5/8/sul2m+xs54UgiDpqGWfTJt20PSEkEC96cbhfXzy6adlu9w8k3hytgIDeshfVtIeYfoRonFcImgZYF5FAxEjee7ZElntpIXYk789x9ofWWq3Vd7BrVDUYqkTHvPYLpnqISynjbXy93t7HGdGBA2Fuf/IQl866bdM0LYDmZ16AQ0q55pyT0XPupHQ2cqvtFFezbaEavVZOTVIjLU6yiBmhYxfdguEcAjgr6ineS2GRbJswYow2w0hZ4s6xROs9wm75frYVh9gkl8sbACAis/S1e5Q8075Q9szHNzCTpL08e65VQrATNuXfJX1ha/oEcrEMDHf2Js4AAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/f7fd5c2f70c6759b61bfcfdfc436ec19/c54d4/commitlint-github-action-comment.webp 175w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/a3432/commitlint-github-action-comment.webp 350w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/426ac/commitlint-github-action-comment.webp 700w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/c139f/commitlint-github-action-comment.webp 1050w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/7f403/commitlint-github-action-comment.webp 1400w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/77646/commitlint-github-action-comment.webp 1867w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/f7fd5c2f70c6759b61bfcfdfc436ec19/4edbd/commitlint-github-action-comment.png 175w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/13ae7/commitlint-github-action-comment.png 350w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/8c557/commitlint-github-action-comment.png 700w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/e996b/commitlint-github-action-comment.png 1050w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/2cefc/commitlint-github-action-comment.png 1400w, /static/f7fd5c2f70c6759b61bfcfdfc436ec19/91bda/commitlint-github-action-comment.png 1867w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/f7fd5c2f70c6759b61bfcfdfc436ec19/8c557/commitlint-github-action-comment.png" alt="Github checks comment" title="Github checks comment" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="commit-message-wizard" style="position:relative;"><a href="#commit-message-wizard" aria-label="commit message wizard permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commit message wizard</h2> <p>Conventions are useful, but what's even more useful is to have some tool, which can help you compose valid commit messages. Especially when your convention is new, there are new joiners or external contributors. Fortunately, there are tools available that help you with crafting compliant commit messages.</p> <h3 id="prompt-cli" style="position:relative;"><a href="#prompt-cli" aria-label="prompt cli permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Prompt CLI</h3> <p>Commitlint offers a tool, basically a command-line wizard, which lets you create your commit messages based on a series of questions. It is called <a href="https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/prompt-cli">@commitlint/prompt-cli</a>. You can easily install it:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">npm install --save-dev @commitlint/prompt-cli</code></pre></div> <p>Now you can register <code class="language-text">commit</code> command in your <code class="language-text">package.json</code>.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">{ &quot;scripts&quot;: { &quot;commit&quot;: &quot;commit&quot; } }</code></pre></div> <p>Now instead of <code class="language-text">git commit</code>, you can run <code class="language-text">npm run commit</code>. It launches an interactive wizard in your console, which will ask you for all the parts of a commit message such as type, subject, and so on.</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">Please enter a type: [required] [tab-completion] [header] &lt;type&gt; holds information about the goal of a change. &lt;type&gt;(&lt;scope&gt;): &lt;subject&gt; &lt;body&gt; &lt;footer&gt; 72 characters left ❯ type:</code></pre></div> <p>What's cool is that this wizard automatically loads your configuration from your <code class="language-text">commitlint.config.js</code> and behaves based on that config.</p> <h3 id="commitizen" style="position:relative;"><a href="#commitizen" aria-label="commitizen permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Commitizen</h3> <p>Another option instead of <code class="language-text">prompt-cli</code> is a third-party solution called <a href="http://commitizen.github.io/cz-cli/">Commitizen</a>.</p> <p>It is also a wizard, which lets you easily create specification-compliant commit messages.</p> <h2 id="custom-configuration" style="position:relative;"><a href="#custom-configuration" aria-label="custom configuration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Custom configuration</h2> <p>If you're not happy with any settings of the predefined conventions, you can override them and provide some more configuration options for linting. This can be done in the <code class="language-text">commitlint.config.js</code> file, which we already created.</p> <p>The detailed configuration is beyond the scope of this article, but you can check <a href="https://commitlint.js.org/#/reference-configuration">the official configuration documentation</a> and the list of <a href="https://commitlint.js.org/#/reference-rules">all available rules</a>.</p> <h2 id="example-repo" style="position:relative;"><a href="#example-repo" aria-label="example repo permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Example repo</h2> <p>You can find an example repository I created with fully set up Commitlint <a href="https://github.com/vojtechruz/commitlint-example/">here</a>. It also includes Travis integration and <code class="language-text">prompt-cli</code> with examples of rejected PRs.</p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>Having convention for commit messages has many benefits, and you can use many automated tools to utilize that. Automatic generation of changelogs and version bumps are no longer a problem. Not to mention much better readability. Commitlint helps you with that both locally and on your Continous Integration server.</p> <p>Husky, used by Commit lint, is a powerful tool for managing git hooks and can be used not only for commit messages but also for a wide variety of other quality checks.</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Stackbit: build JAMStack sites in a few clicks]]>https://www.vojtechruzicka.com/stackbit/https://www.vojtechruzicka.com/stackbit/Thu, 07 Nov 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 53.14285714285714%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAACbklEQVQozz3SW0/aYBzH8b6BHSq05WCBUpCWPpRCaanouFmWzWyJiXNTiCdARXCCA3EIKJ6Y9YAHZNOZJRrdhVtcsiXbwpZd7GrJdrlXs8u1kOwFPPnk+X3/0E3qMUyH2ughDTOqZSIIGEddcR2b1LtnDNyskcu2e3K4N2/ii2bfksW3QgjrVnGD9G/ZpB0Ipgfb6LDGOaxlxhAmhromMVdCx07r3SkD99TomWv3PsO9BRNfMvuWLcIqIVZIUbb5t+3SLqSyziENGNMCldWxU3r3tIFLNdkMzs+b+AWzr2gRyoSwYhXXSUkm/ZsKa5f2IIVtc4RhcgBxRmA6dt0eu2abgKkEClJ6NgNTswiTQ0EeBQWDe0nHLmNglRS37FK1o3MfgqmQTkoTDw+0nmkmmH0wsvNo8rAnXKWDRVIqhBKve8L1OwP1wfjZ7f6j/vHz8ewVE9yz+Xc7OmsQwsfZ0o/g1V96+df94e366bfkwnkyf3H58ffWi0YscyHXGicXP+Xa9/ef/5TkRmHja4t1BA4hLRiz9O2C7Be8d1+4VyzKV929lVDiVUn+kC69TZXeRdJvJucub4XPcquf+iLndwdPrULVEag5AnVI4xzVOEYQZxShospUGDuDudJ6LnvDnkJBjhCWzL6ykSvbpA3cWyGETVKs2pssFXgJqW1dalvMndSxT/TutDKy0tbkU0c28YtKIUJcI4TK/5GV3yos1XUEISCKggnMNaW2ZVttlcOYx70LzbZlta16GHLzMFS2o1Nl6a5j5XEMBXGMTbRYA5cxenLt3jzOF8z8okVhhTWr+Jz0K4fRYg9aLN198g8ivcgsqV2NqQAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/fc0132493513898f2d10e0b217466388/c54d4/stackbit.webp 175w, /static/fc0132493513898f2d10e0b217466388/a3432/stackbit.webp 350w, /static/fc0132493513898f2d10e0b217466388/426ac/stackbit.webp 700w, /static/fc0132493513898f2d10e0b217466388/c139f/stackbit.webp 1050w, /static/fc0132493513898f2d10e0b217466388/7f403/stackbit.webp 1400w, /static/fc0132493513898f2d10e0b217466388/83c15/stackbit.webp 1764w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/fc0132493513898f2d10e0b217466388/4edbd/stackbit.png 175w, /static/fc0132493513898f2d10e0b217466388/13ae7/stackbit.png 350w, /static/fc0132493513898f2d10e0b217466388/8c557/stackbit.png 700w, /static/fc0132493513898f2d10e0b217466388/e996b/stackbit.png 1050w, /static/fc0132493513898f2d10e0b217466388/2cefc/stackbit.png 1400w, /static/fc0132493513898f2d10e0b217466388/a2498/stackbit.png 1764w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/fc0132493513898f2d10e0b217466388/8c557/stackbit.png" alt="Stackbit" title="Stackbit" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>How to easily create full-blown JAMStack sites with a theme, CMS, and deployment using various static site generators.</p> <h2 id="jamstack" style="position:relative;"><a href="#jamstack" aria-label="jamstack permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>JAMStack</h2> <p>JAMStack is a powerful concept of building static sites. It is an alternative to having a traditional heavyweight CMS, like WordPress, which is also responsible for dynamically creating and serving your content.</p> <p>While tools like WordPress are powerful, they are much slower than static sites and can be easily hacked unless you diligently keep everything up to date.</p> <p>When you build your sites statically, you can easily distribute them via a CDN for much better performance and utilize stronger caching. Since everything is static, you don't need to worry about your system being hacked as there are no moving parts. Everything is also much cheaper to run and host.</p> <p>You can learn more about JAMStack and its benefits in one of my previous posts on how I migrated from WordPress to GatsbyJS:</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/gatsby-migration/" style="box-shadow: none;">Migration to GatsbyJS and JAM stack from WordPress</a></h4><small class="front-post-info"><span class="front-post-info-date">25 March, 2018</span><div class="post-tags"><ul><li><a href="/tags/blogging/">#Blogging</a></li><li><a href="/tags/jam-stack/">#JAMStack</a></li></ul></div></small><div><a class="front-post-image" href="/gatsby-migration/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 66.6667%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABAD/xAAVAQEBAAAAAAAAAAAAAAAAAAACBP/aAAwDAQACEAMQAAABK1R50KdE/wD/xAAbEAEAAgIDAAAAAAAAAAAAAAACAAEEERITFP/aAAgBAQABBQIE8n1y7O/OIsU2qxhP/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAECEv/aAAgBAwEBPwFQYZ//xAAVEQEBAAAAAAAAAAAAAAAAAAAQcf/aAAgBAgEBPwGn/8QAGRAAAwEBAQAAAAAAAAAAAAAAAAERIRIx/9oACAEBAAY/Ao0J5R+GKMvTN1n/xAAbEAEAAgIDAAAAAAAAAAAAAAABABExUSGRwf/aAAgBAQABPyElC3tgsPZ5LUmUbeTZcAEUbgTPcz//2gAMAwEAAgADAAAAEIc//8QAFhEBAQEAAAAAAAAAAAAAAAAAAFFh/9oACAEDAQE/EKNn/8QAGREAAgMBAAAAAAAAAAAAAAAAAAERITFB/9oACAECAQE/EE50Uun/xAAeEAEBAAICAgMAAAAAAAAAAAABEQAhMYFBUbHB8P/aAAgBAQABPxBGrRCxfKp8ZHuqI/j7xyIqdmJSgsKZ06yMQDh374yaNVWRejP/2Q==" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0; transition-delay: 500ms;"><picture><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jam/5e4a3/jam.jpg 45w, /linked/jam/e451c/jam.jpg 90w, /linked/jam/29fd0/jam.jpg 180w, /linked/jam/b3ebb/jam.jpg 270w, /linked/jam/8841e/jam.jpg 360w, /linked/jam/95b54/jam.jpg 540w, /linked/jam/2b1a3/jam.jpg 900w" src="/linked/jam/29fd0/jam.jpg" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: opacity 500ms ease 0s;"></picture><noscript><picture><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jam/5e4a3/jam.jpg 45w, /linked/jam/e451c/jam.jpg 90w, /linked/jam/29fd0/jam.jpg 180w, /linked/jam/b3ebb/jam.jpg 270w, /linked/jam/8841e/jam.jpg 360w, /linked/jam/95b54/jam.jpg 540w, /linked/jam/2b1a3/jam.jpg 900w" src="/linked/jam/29fd0/jam.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">How and Why I migrated from WordPress to static JAM Stack site built with Gatsby JS.</span></div></div> <h2 id="building-static-sites" style="position:relative;"><a href="#building-static-sites" aria-label="building static sites permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Building Static sites</h2> <p>While JAMStack-powered sites have many advantages, there are drawbacks as well. The main disadvantage is that it takes much more effort to build a full-fledged site including theme, Content Management System and deployment.</p> <p>With WordPress, you can have a running site in minutes with a little effort. Even non-developers can easily create and maintain a site. Not so much with JAMStack. You have to select and learn your <a href="https://www.staticgen.com/">static site generator</a>. Then choose a theme and make sure it works well with your generator. Then choose <a href="https://headlesscms.org/">one of many available CMSs</a>. Then figure how to set up your deployment pipeline. </p> <p>It takes a lot of effort for developers, and it is nearly impossible for somebody not so tech-savvy. At least it was until now.</p> <h2 id="stackbit" style="position:relative;"><a href="#stackbit" aria-label="stackbit permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Stackbit</h2> <p><a href="https://www.stackbit.com/">Stackbit</a> is a service, which solves the biggest pain point of static sites described above. It is a wizard, which lets you build and deploy a fully functioning static site using one of the supported static generators, with a theme, CMS and a functional deployment.</p> <h2 id="creating-a-new-site" style="position:relative;"><a href="#creating-a-new-site" aria-label="creating a new site permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Creating a new site</h2> <p>New sites are created with a wizard in four simple steps.</p> <h3 id="1-selecting-a-theme" style="position:relative;"><a href="#1-selecting-a-theme" aria-label="1 selecting a theme permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>1. Selecting a theme</h3> <p>The first step is to select a theme for your new site. There are currently 14 built-in themes, with 23 more marked as coming soon.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 46.857142857142854%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAACFklEQVQozz1RW2sTQRgdi2KplGBj8dJirVKoNaaJkKClBBMrBX+C/8FHwbcWBFNpBfXBFqWpguKlD4q+JFCK4gVvbWzSFkObrDb3Tfa+s7O7Mzvjpg9+fC/fgfOdwzlg+Vf+c2Yrnc39zOS289wajxObuMqLVV4gDvu/mFCHMkHSclzxfX5DUDXGGLg7/+bOw8V788/vJ14+efH6R8WMr+CGpNUboqjoTVHlRbUpabKGbEJFxVjf4pKljGIYlDLQfnx0X/9oW28QHA10nrogK62XyCKGiSGyLRMRyyAmckFXGZkOhKauO7xq25iCzlDMGxrrPhPd2xfq9l8SZR2bBCELWRhj+r1IUwWWqrB3O/i3QikliBDLIRZlLfKJs+f7gpHDwfEDAyNdvqikQMqYQbCrxCi7skDBFLv6bDWeyk7lafnP9rfs+ipX5krFXeWBsGdwxHM6sr8/7B0ea9aE7enZjRu3ri98epou31xiw4/pZDI3sSbPldjbxVeT8dszD2aXllOuL9DWEwTHAnt6z4Ej/kMuuSpsTs+lJ2auPfqYWKkQ2+ElrajYNYMaFpEkuVavff3yga/9hRYDHv/lrsD4waGIy/f6ooKsN1TddsvZHVWFhZ0yRK3AXJ/lerNQqkOjdSY5G3QMxToGL7afDIOeoNcXcwNzc5RUXYPQxo5lE91AOjQch7pVQ8OEyFQ03TStkmT9A6RlkJyN8KAfAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/191d5118b803759d580c5be85506df0d/c54d4/stackbit-theme.webp 175w, /static/191d5118b803759d580c5be85506df0d/a3432/stackbit-theme.webp 350w, /static/191d5118b803759d580c5be85506df0d/426ac/stackbit-theme.webp 700w, /static/191d5118b803759d580c5be85506df0d/c139f/stackbit-theme.webp 1050w, /static/191d5118b803759d580c5be85506df0d/7f403/stackbit-theme.webp 1400w, /static/191d5118b803759d580c5be85506df0d/70734/stackbit-theme.webp 2920w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/191d5118b803759d580c5be85506df0d/4edbd/stackbit-theme.png 175w, /static/191d5118b803759d580c5be85506df0d/13ae7/stackbit-theme.png 350w, /static/191d5118b803759d580c5be85506df0d/8c557/stackbit-theme.png 700w, /static/191d5118b803759d580c5be85506df0d/e996b/stackbit-theme.png 1050w, /static/191d5118b803759d580c5be85506df0d/2cefc/stackbit-theme.png 1400w, /static/191d5118b803759d580c5be85506df0d/751eb/stackbit-theme.png 2920w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/191d5118b803759d580c5be85506df0d/8c557/stackbit-theme.png" alt="Selecting a theme" title="Selecting a theme" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>However, on top of built-in themes, you can <a href="https://www.stackbit.com/blog/announcing-custom-themes-and-so-much-more/">import your own</a>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 41.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAABNElEQVQY032Qy07EMAxF+/8fxY4FwwaQRixgqKaPpE2njzRO4rRJjGdALJBAurrywse+djFMupFD24/nthf92EjFRS2Hsu7AIRHtkWKimGkxtu0v3HmqpFRTzlQw/FGJWij2SqiykR+1LGv5VjZq0vueUqY9ZfbTWd7dPx6ejv0wzYu5wrglDX4FNC6wO+R+2m7bcIusbc8xZd4/w/6ubLeGwVEHCUIqECMYb4wH5nmK8RYQwIPFZTVCqrYbODCPM25TM0yr0zZMxruwF9rjaK12/iacrR8AJmsXh0p7OZpJg+U8X5enHGPmwJmIDykelvPLKnsE4VfhDRcshXYO2+hpRAqJuPtG/lZx0OWzaVQAiVqgvgQbYuTJOdHV/8C+4eNsXxfXudBYFC5UcK35Zz/6B/4EogrJcDTRcpAAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/111f88c356b12777f79f04b99db48da6/c54d4/stackbit-theme-import.webp 175w, /static/111f88c356b12777f79f04b99db48da6/a3432/stackbit-theme-import.webp 350w, /static/111f88c356b12777f79f04b99db48da6/426ac/stackbit-theme-import.webp 700w, /static/111f88c356b12777f79f04b99db48da6/c139f/stackbit-theme-import.webp 1050w, /static/111f88c356b12777f79f04b99db48da6/7f403/stackbit-theme-import.webp 1400w, /static/111f88c356b12777f79f04b99db48da6/af976/stackbit-theme-import.webp 2039w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/111f88c356b12777f79f04b99db48da6/4edbd/stackbit-theme-import.png 175w, /static/111f88c356b12777f79f04b99db48da6/13ae7/stackbit-theme-import.png 350w, /static/111f88c356b12777f79f04b99db48da6/8c557/stackbit-theme-import.png 700w, /static/111f88c356b12777f79f04b99db48da6/e996b/stackbit-theme-import.png 1050w, /static/111f88c356b12777f79f04b99db48da6/2cefc/stackbit-theme-import.png 1400w, /static/111f88c356b12777f79f04b99db48da6/4cd27/stackbit-theme-import.png 2039w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/111f88c356b12777f79f04b99db48da6/8c557/stackbit-theme-import.png" alt="Importing Custom Theme" title="Importing Custom Theme" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Traditionally, there was a problem of using themes with a static site generator as each theme is generator specific and cannot be easily shared between generators. This is where Stackbit really shines as it allows you to create universal themes independent on a generator. This way, it makes it easy to reuse a theme when migrating to a new generator.</p> <p>To achieve this, Stackbit defines <a href="https://docs.stackbit.com/uniform/">Uniform theme model</a> and then offers a tool called <a href="https://docs.stackbit.com/unibit/">Unibit</a> to convert it from this model to a theme specific for any supported generator. This means if a new generator is introduced in the future, you can use your old theme with it.</p> <h3 id="2-selecting-a-static-site-generator" style="position:relative;"><a href="#2-selecting-a-static-site-generator" aria-label="2 selecting a static site generator permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>2. Selecting a static site generator</h3> <p>The second step is selecting your generator. No matter which one you choose, it will work with your theme and CMS seamlessly.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 47.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAB1ElEQVQoz1VQy27TUBA1IPGoQtOKBapKWQAlbaCkIFBRIggpIFjDvyDBrguksoIFLMiXdIvEAgkQhBJaR5BHUxvHdmL7Pnzt+/Bl0kggRkejGd17zswZI7fyoFB9tHzn8YlCNV+sWo6ntZYqy/Q4VAb1f2CJiFkSs5QLZRxbXD+6vH5k4ZoxXTg5X9reHWzvaMsZdvedTv93iCjwhcz+IhUq5RKYIGQ8eV5/+qK+8bL+bPPNxqu33T559167PrYGPmAYIETiCNMQU8gRooiwCMd+gEDCaHVGOz+dZqv93WzDNAGCWmOajEICnzhXnMO0MThkDmNVwiVLpZDamLl593Tl4Vzp/tSF8kyx2rNcIGcHlpXKuJx4HwfU6l+nudDG2ZW1M6uV/KXa8XNrU4vlPdvNlGx3uruNxqDT1jb+vGW+bvV/2Vs6tX98sr59bH5tfDFNU4Dn3Pkb+aXKqcu13MXy9NKtPcsVnLu+7/R6eN/KmAws1IwIon0laODSkRd4Hrz7HNY+NH/VmFs9yKUJmcSMxmy8vNKYpQFGkz3hGhGBQ5BJm3Bl5Iq12Sv3Zou3Dy9cBzJ4jhAZBiFLEikVjbnrDUFOCAGG/VHouH6cJHCQD7b4A23hpWCxmMSAAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/068453fc05b0752a75af31041afcc0b7/c54d4/stackbit-generators.webp 175w, /static/068453fc05b0752a75af31041afcc0b7/a3432/stackbit-generators.webp 350w, /static/068453fc05b0752a75af31041afcc0b7/426ac/stackbit-generators.webp 700w, /static/068453fc05b0752a75af31041afcc0b7/c139f/stackbit-generators.webp 1050w, /static/068453fc05b0752a75af31041afcc0b7/7f403/stackbit-generators.webp 1400w, /static/068453fc05b0752a75af31041afcc0b7/eb1f9/stackbit-generators.webp 2892w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/068453fc05b0752a75af31041afcc0b7/4edbd/stackbit-generators.png 175w, /static/068453fc05b0752a75af31041afcc0b7/13ae7/stackbit-generators.png 350w, /static/068453fc05b0752a75af31041afcc0b7/8c557/stackbit-generators.png 700w, /static/068453fc05b0752a75af31041afcc0b7/e996b/stackbit-generators.png 1050w, /static/068453fc05b0752a75af31041afcc0b7/2cefc/stackbit-generators.png 1400w, /static/068453fc05b0752a75af31041afcc0b7/fcdaa/stackbit-generators.png 2892w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/068453fc05b0752a75af31041afcc0b7/8c557/stackbit-generators.png" alt="Selecting a generator" title="Selecting a generator" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Stackbit currently supports the following static site generators:</p> <ul> <li>Jekyll</li> <li>Gatsby</li> <li>Hugo</li> </ul> <p>Two more are marked as coming soon: VuePress and Hexo. They are likely to add more generators in the future.</p> <h3 id="3-selecting-a-cms" style="position:relative;"><a href="#3-selecting-a-cms" aria-label="3 selecting a cms permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>3. Selecting a CMS</h3> <p>With JAMStack, you don't have to use any Content Management System at all. Your articles are usually saved in your git repo in the form of Markdown files or a similar format. You can create and edit your content directly in your IDE.</p> <p>However, usually, content creators are non-technical people, so you need to integrate some kind of CMS for them, so they can easily create and edit content using a nice and simple GUI.</p> <p>As a third step of the project creation workflow, Stackbit allows you to choose one of the supported CMSs, which will be used to manage the content on your site.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 46.857142857142854%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAB10lEQVQoz1WQy27TQBSGHaBCVCBaikBcKoooVG2jChK1IKhcBbqoAIXLNosKVLHKAvoWPAqPwB4kNmyyckqTNIljOzPj2LF8mbs5BgmJo5kzOmdm/vnmN86t7S5vv75nvpm58+TiqukiP89zpXT+f0ANPalyynia0TRjXChjbvPV1Uf1hfWd0mL1SvnZj/7RF/LTQX5v6HYHDowT2+uP0FHPxv4UJCiTlAnKpZDaOHvbnF2pXd6ozSxtLdzd/t63PttfcRghEo5J4KIJZA8HQwePPBKnNKMipRwy48p4sHfwsP5h80VjZedt9XkDkQAgQVWpAlLIIsPgQmdMCVH0//LDltENyTHyHNefTGPQA544SdKsCAovACEs8D8thFRUacpYksI+HMyNg+bH/ebhy/eHe41m/d0n4Ox0Ou1227IsTIjr4VarhRAKY4fJmHio0z2x2r8gc5Ebp6+Vz9yqzC4/Lt2snF/aGrq48FbrP57nwPnPbyDXgFsYX8wC+9RitXSjAjeN6/cvlWtDj4yJ74yx1goUwijtDezuYAQ/gRL5wXFvEEwjuA8WGBfWn85v7M6vmaAyt2raLoFzeBJGccK5TDLuB1OokpTCU1GcIjIBd3Itv9n8NyhZpQnVoh+pAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/66265b5a79c0f75d1e837e12a7255368/c54d4/stackbit-cms.webp 175w, /static/66265b5a79c0f75d1e837e12a7255368/a3432/stackbit-cms.webp 350w, /static/66265b5a79c0f75d1e837e12a7255368/426ac/stackbit-cms.webp 700w, /static/66265b5a79c0f75d1e837e12a7255368/c139f/stackbit-cms.webp 1050w, /static/66265b5a79c0f75d1e837e12a7255368/7f403/stackbit-cms.webp 1400w, /static/66265b5a79c0f75d1e837e12a7255368/c3b43/stackbit-cms.webp 2895w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/66265b5a79c0f75d1e837e12a7255368/4edbd/stackbit-cms.png 175w, /static/66265b5a79c0f75d1e837e12a7255368/13ae7/stackbit-cms.png 350w, /static/66265b5a79c0f75d1e837e12a7255368/8c557/stackbit-cms.png 700w, /static/66265b5a79c0f75d1e837e12a7255368/e996b/stackbit-cms.png 1050w, /static/66265b5a79c0f75d1e837e12a7255368/2cefc/stackbit-cms.png 1400w, /static/66265b5a79c0f75d1e837e12a7255368/da716/stackbit-cms.png 2895w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/66265b5a79c0f75d1e837e12a7255368/8c557/stackbit-cms.png" alt="Selecting a CMS" title="Selecting a CMS" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>If you don't want to use any CMS, you can skip this step. It is always possible to add a CMS later.</p> <p>Stackbit currently supports:</p> <ul> <li>Forestry</li> <li>Netlify CMS</li> <li>Contentful</li> <li>DatoCMS</li> <li>Sanity</li> </ul> <p>Prismic is marked as coming soon.</p> <h3 id="4-deployment" style="position:relative;"><a href="#4-deployment" aria-label="4 deployment permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>4. Deployment</h3> <p>The last step is to select a deployment method. That means:</p> <ol> <li>Selecting a git repository, where your project will be created</li> <li>Selecting a service which will be used for deployment</li> </ol> <p>As a git repository hosting, you can currently use GitHub, but GitLab and Bitbucket are coming soon.</p> <p>As a deployment method, there is currently offered just Netlify. It is a great service, which I currently use for my blog, you can read more details about it in this post:</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/jamstack-migration-netlify/" style="box-shadow: none;">Migration to JAM stack and Netlify from WordPress</a></h4><small class="front-post-info"><span class="front-post-info-date">21 May, 2018</span><div class="post-tags"><ul><li><a href="/tags/blogging/">#Blogging</a></li><li><a href="/tags/jam-stack/">#JAMStack</a></li></ul></div></small><div><a class="front-post-image" href="/jamstack-migration-netlify/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 66.6667%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABYBAQEBAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAABdxcMyOUQv//EABkQAAIDAQAAAAAAAAAAAAAAAAABAhESIf/aAAgBAQABBQJ1U0ZK5lEl3//EABYRAQEBAAAAAAAAAAAAAAAAAAASE//aAAgBAwEBPwGGb//EABcRAQADAAAAAAAAAAAAAAAAAAABAxP/2gAIAQIBAT8Bmxs//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAERIDH/2gAIAQEABj8CHBlP/8QAGBABAQEBAQAAAAAAAAAAAAAAAREAITH/2gAIAQEAAT8hkVFFnDSADU+jSLMPLO4Wt//aAAwDAQACAAMAAAAQ7B//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8QQmQMv//EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxATY//EABoQAQEAAwEBAAAAAAAAAAAAAAERACExQWH/2gAIAQEAAT8QGr3lt/cFhPBDmPvRruLMUpdcwLohFzRkpYZ//9k=" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0;"><picture><source srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" sizes="(max-width: 180px) 100vw, 180px"><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" src="/linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg" alt="" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: none 0s ease 0s;"></picture><noscript><picture><source srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" sizes="(max-width: 180px) 100vw, 180px" /><img loading="lazy" sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" src="/linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">How and Why I migrated from WordPress to static JAM Stack site deployed on Netlify.</span></div></div> <p>GitHub Pages and GitLab pages are going to be supported soon, as well. </p> <p>Once you authorize your GitHub and Netlify access, you're done. A git repo is created for you, and the project is automatically deployed.</p> <h2 id="dashboard" style="position:relative;"><a href="#dashboard" aria-label="dashboard permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Dashboard</h2> <p>When you finish creating your first project, it becomes available on your main dashboard, where you can see and manage all the projects. You have a single place from which you can edit your settings, navigate to your site, CMS, or git repository. </p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 44.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABY0lEQVQoz42Pz0oCURTG5wGSaNUiaGu1CXdRLrJN2wiCaNuuR+gFopeoXVGUbipatHFTiQvJTEVrHBxHNMccR+fPPffec7s6YJERffw4i8P5ON+nTC9tRNZ35mJbkwuxUHglFI7+wUQ4OjUfnVlcnY2syankStpLSc0WXnNFtVyplbUhFX2cUkVXNT35XD15qJ6lqol0Vel0e0bTNBpmq22hEBwH/KpgH38Su6e4d4H716iw0SkipRwAfWBAkbJxBBvi+wMAhAJyS9Fh2AJBGPO4EzwJ7hj/ggDredT1wQdKgMqpWA5Gj8Xm5ftBshJvOHoj9dE2GfV/ZEZEoLRQA7NjO67r+USiWH2+fCS2z/XDdC3REnkNbvP9rEHSOlPbg0qjWkBZw2oalte0iR+Y2aA0d4F3qZAV7or8Kof3Kt7kWKbGv5tlbLtTfzPddp9IuZ40c/FPyZKZumdYxHYJA/JoeJ8kTcq14/OFnwAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/6690e4e9246aff7263a6649a42a6de56/c54d4/stackbit-dashboard.webp 175w, /static/6690e4e9246aff7263a6649a42a6de56/a3432/stackbit-dashboard.webp 350w, /static/6690e4e9246aff7263a6649a42a6de56/426ac/stackbit-dashboard.webp 700w, /static/6690e4e9246aff7263a6649a42a6de56/c139f/stackbit-dashboard.webp 1050w, /static/6690e4e9246aff7263a6649a42a6de56/7f403/stackbit-dashboard.webp 1400w, /static/6690e4e9246aff7263a6649a42a6de56/46779/stackbit-dashboard.webp 2950w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/6690e4e9246aff7263a6649a42a6de56/4edbd/stackbit-dashboard.png 175w, /static/6690e4e9246aff7263a6649a42a6de56/13ae7/stackbit-dashboard.png 350w, /static/6690e4e9246aff7263a6649a42a6de56/8c557/stackbit-dashboard.png 700w, /static/6690e4e9246aff7263a6649a42a6de56/e996b/stackbit-dashboard.png 1050w, /static/6690e4e9246aff7263a6649a42a6de56/2cefc/stackbit-dashboard.png 1400w, /static/6690e4e9246aff7263a6649a42a6de56/c6f3b/stackbit-dashboard.png 2950w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/6690e4e9246aff7263a6649a42a6de56/8c557/stackbit-dashboard.png" alt="Dashboard" title="Dashboard" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p> But the dashboard is not the only place you can manage your site from. If you go directly to your site and are logged in to Stackbit, you can see a special Stackbit button, where you can manage the site directly without going to your dashboard.</p> <p> <span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 57.714285714285715%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAAAsSAAALEgHS3X78AAACnklEQVQoz02QW0iTcRjG/5lolESJjZihZRBEtwZlBgZBURTYRVfLm7ooqKuiQqjAzGO6dJpzajpTp7M5jzlPM9MiUCIPJe2gJh62OXdw27d9+77v//RNIXp5eHkv3h8Pz0NmHNTjcm5ubAQCAYhDKf6NeFMhwHJuJiJvkNsKhj1B3vBb0P4Q3AFKhheFeevKnGl5dcPL8PCx1B+GKF8Y4t+6N/RoLHDHyN81srcGOdkAKxvkLncK94f4qXWOHC9GshzJChxRQFqJRCWkNZDWQ9qIRA0SW7nTrdx1Pb2mR4aWZrTRNA09p6EXPwgpYywhj0GeUPKMkpeU5IEUgJSClIG8BakBUYVfTIQHLFzHPNdrEvostNtMxV39PbzXwJB0JU6V44QCJ5W4pMXhSpASRFUiSglSi6Oa0PhSEEIwyAQ4lgHPUp4Vy3B4vBKDk6RXI7UKqSqcbcCNDqS1IEYBUgGiRHQDzveGvlocXoYzO3yzi7Zpy+rcH7vDG7LaPZIBJ0kqQEI+4vIhLYO0AgmVOzAlVTjz3pqtH500LTt97PyKc3ZhbUaEF202D2O1uyOwJBcHXiG+CAdLEFeKGDGwHFEVggirm4u8jRlmy3yIwrbptbv96y7fmssXDNMVt/+QCCcXQloYKTxJgYRyxMhB3mzDStQ1FbrUF5p1HX2fJ3uMX/RD46L6RsZHRid0/UZJv53E5yA+D/vyEFuA/XLEyv93LoDuapNWo9INqrsGqtp6att7H77rzlL1ZtaP7Bn2E1kbvdnKZ+npMbGkXOx6veMcyfy8uWXp49OuzsbaquJ+4+AmB5d7SzYDYsDuTyATIL9WmW9mj8nGZGp5koPokgi803ZUHVJ0eKDSye7dzla3t/10tE8tXJnehsci8F9tOfN83zpILwAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/ed67519cec00ee075970dc3f706b1f3b/c54d4/stackbit-button.webp 175w, /static/ed67519cec00ee075970dc3f706b1f3b/a3432/stackbit-button.webp 350w, /static/ed67519cec00ee075970dc3f706b1f3b/426ac/stackbit-button.webp 700w, /static/ed67519cec00ee075970dc3f706b1f3b/c139f/stackbit-button.webp 1050w, /static/ed67519cec00ee075970dc3f706b1f3b/7f403/stackbit-button.webp 1400w, /static/ed67519cec00ee075970dc3f706b1f3b/63a83/stackbit-button.webp 2332w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/ed67519cec00ee075970dc3f706b1f3b/4edbd/stackbit-button.png 175w, /static/ed67519cec00ee075970dc3f706b1f3b/13ae7/stackbit-button.png 350w, /static/ed67519cec00ee075970dc3f706b1f3b/8c557/stackbit-button.png 700w, /static/ed67519cec00ee075970dc3f706b1f3b/e996b/stackbit-button.png 1050w, /static/ed67519cec00ee075970dc3f706b1f3b/2cefc/stackbit-button.png 1400w, /static/ed67519cec00ee075970dc3f706b1f3b/5cae2/stackbit-button.png 2332w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/ed67519cec00ee075970dc3f706b1f3b/8c557/stackbit-button.png" alt="Stackbit Button" title="Stackbit Button" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="medium-import" style="position:relative;"><a href="#medium-import" aria-label="medium import permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Medium import</h2> <p> A few years back, Medium got very popular as a blogging platform. These days, with the recent changes, many people are migrating back from Medium. Hosting your own static site for your blog is a viable alternative.</p> <p> With Stackbit, migration from Medium is fortunately effortless. There is a <a href="https://www.stackbit.com/medium/">dedicated page</a>, where you can upload your Medium export archive, and it will automatically create a new Stackbit project for you with your data.</p> <p> <span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 45.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABNklEQVQoz32Q7VbCMAyGd//XItyLehAFVxzDDVTotn6tadOYDgH9Y/qcnqbnvEneFIuX6mG5uV+UotrXzVFUh039UbdHCOQhhUDOwcmA9CTHzJdNPibik6hYb5qn1bbafWkTDGODtTHfI45jipGs8502nRmV9Z1x2odEP1GU1eFVNNvd56p857Zr0ZRvLb+bvWQ9i431g1JMPwxam8hfN7Go61b2Crizc9E5zIwIkAAIIvXGS2WYU69GCCwNmEEeu9ntO0tSo3V5TjYZIMOewZMP1zbkAkqb8zThkQr2jTGTZSyAX1zEaXLZSve47a7pGLMY6W/kckgRc62zGJHNR0jEa+YHp1nMnenfONvj/ToIg7GDttYDpxkWD4pXaJU2WttzydsIEzhNKYSYzeZ3s/ly+UyXz2+oKggDcAuOtwAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/e9cd37aa150ad706f185f95c953ae3b2/c54d4/medium-migration.webp 175w, /static/e9cd37aa150ad706f185f95c953ae3b2/a3432/medium-migration.webp 350w, /static/e9cd37aa150ad706f185f95c953ae3b2/426ac/medium-migration.webp 700w, /static/e9cd37aa150ad706f185f95c953ae3b2/c139f/medium-migration.webp 1050w, /static/e9cd37aa150ad706f185f95c953ae3b2/7f403/medium-migration.webp 1400w, /static/e9cd37aa150ad706f185f95c953ae3b2/db9e1/medium-migration.webp 2669w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/e9cd37aa150ad706f185f95c953ae3b2/4edbd/medium-migration.png 175w, /static/e9cd37aa150ad706f185f95c953ae3b2/13ae7/medium-migration.png 350w, /static/e9cd37aa150ad706f185f95c953ae3b2/8c557/medium-migration.png 700w, /static/e9cd37aa150ad706f185f95c953ae3b2/e996b/medium-migration.png 1050w, /static/e9cd37aa150ad706f185f95c953ae3b2/2cefc/medium-migration.png 1400w, /static/e9cd37aa150ad706f185f95c953ae3b2/b9139/medium-migration.png 2669w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/e9cd37aa150ad706f185f95c953ae3b2/8c557/medium-migration.png" alt="Medium migration" title="Medium migration" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <h2 id="devto-integration" style="position:relative;"><a href="#devto-integration" aria-label="devto integration permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>DEV.to integration</h2> <p> Services such as Medium are no doubt convenient to use, and you can reach a much larger audience than on your own. There is one major drawback, though. You don't really own your content and your audience. When such service goes out of business or changes its pricing model, you lose your content and audience. It is much better to have your own site so that you can build a stable audience, and you're safe from any changes of third-party services.</p> <p>It would be great if you could combine the advantages of both approaches. It turns out you can! With Stackbit and <a href="https://dev.to/">DEV.TO</a> integration.</p> <p>DEV.to is an excellent service, focused purely on developers, which lets you publish your content to reach a wider audience. It already offers you a possibility to automatically publish to DEV.to using your RSS feed. So you still have your content hosted on your site, and it adds a canonical URL to your original site, so you do not cannibalize your SEO.</p> <p>Now with Stackbit, it is possible also the other way around. You can <a href="https://www.stackbit.com/blog/devto-stackbit/">publish your existing DEV.to content to your Stackbit site</a>.</p> <h2 id="beta" style="position:relative;"><a href="#beta" aria-label="beta permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Beta</h2> <p> Stackbit is already publicly available and functional, but it is still running in open beta. Not all the features may be final, and there are many more features and supported services to be added soon. Go ahead and check <a href="https://www.stackbit.com/blog/">their blog</a> periodically to stay informed about the new features.</p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p> Stackbit is a very powerful tool, which lets you create a fully functional JAMStack site with all the bells and whistles just in minutes using a simple wizard. The main disadvantage of JAMStack - the high barrier of entry and accessibility to only technical people applies no more. Go ahead and give it a try!</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Comparing files and folders in IntelliJ IDEA]]>https://www.vojtechruzicka.com/idea-compare-files-and-folders/https://www.vojtechruzicka.com/idea-compare-files-and-folders/Fri, 25 Oct 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/828b01d29e0d17aa655b9f878221b7e6/b17f8/idea-compare.jpg" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAQFAwb/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAG4mxAOkMg//8QAGxAAAgIDAQAAAAAAAAAAAAAAAAECBAMREiL/2gAIAQEAAQUCIZepjSkq3qzs/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAHBAAAQQDAQAAAAAAAAAAAAAAAQAQETECIUKh/9oACAEBAAY/Al1vxoNFHA0Jb//EABsQAQACAgMAAAAAAAAAAAAAAAEAURARMUGB/9oACAEBAAE/IVg9I3AaXU3EB75COwQPcP/aAAwDAQACAAMAAAAQ9w//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAbEAEAAwADAQAAAAAAAAAAAAABABEhEDFBUf/aAAgBAQABPxAQtaIVQCBYnqPriTElJ6SstoQpoK93qKGf/9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/828b01d29e0d17aa655b9f878221b7e6/c54d4/idea-compare.webp 175w, /static/828b01d29e0d17aa655b9f878221b7e6/a3432/idea-compare.webp 350w, /static/828b01d29e0d17aa655b9f878221b7e6/426ac/idea-compare.webp 700w, /static/828b01d29e0d17aa655b9f878221b7e6/c139f/idea-compare.webp 1050w, /static/828b01d29e0d17aa655b9f878221b7e6/7f403/idea-compare.webp 1400w, /static/828b01d29e0d17aa655b9f878221b7e6/fad48/idea-compare.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/828b01d29e0d17aa655b9f878221b7e6/e52aa/idea-compare.jpg 175w, /static/828b01d29e0d17aa655b9f878221b7e6/70ebb/idea-compare.jpg 350w, /static/828b01d29e0d17aa655b9f878221b7e6/29d31/idea-compare.jpg 700w, /static/828b01d29e0d17aa655b9f878221b7e6/9ecec/idea-compare.jpg 1050w, /static/828b01d29e0d17aa655b9f878221b7e6/d165a/idea-compare.jpg 1400w, /static/828b01d29e0d17aa655b9f878221b7e6/b17f8/idea-compare.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/828b01d29e0d17aa655b9f878221b7e6/29d31/idea-compare.jpg" alt="IntelliJ IDEA compare files and folders" title="IntelliJ IDEA compare files and folders" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>IntelliJ IDEA offers numerous ways of comparing files, folders and code snippets and even synchronizing folder contents.</p> <h2 id="comparing-project-files" style="position:relative;"><a href="#comparing-project-files" aria-label="comparing project files permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Comparing project files</h2> <p>Let's say there are two similar files in your project, and you need to compare them line by line. With IDEA, that's very easy. Just select both files in your project window (holding <kbd>Ctrl</kbd> for multi-selection).</p> <p>Now you have two options:</p> <ol> <li>Right click one of the files and select <code class="language-text">Compare Files</code></li> <li>Press <kbd>Ctrl</kbd> + <kbd>D</kbd></li> </ol> <p>Now the new window opens, which contains two panels, each with one file. This is very similar to diff in Version Control Systems such as Git.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/49b43b99192d60e480e03ab0b87f35c6/d185f/idea-compare-files.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 47.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABs0lEQVQoz02R3XLaMBCF/f7P05kmtGmn0JIQAk6MAWPLkiXZkvwjy5LBJmCgSi46PfPt3pyd3dldRwhOKY1BhBOIMYpjgGAchTtCMMEoF6wqS900xmijP3LTKIuqJefMSVOR8XIb0R1kuzjDlFtolhdVEyc5oiqhRcZyTAVJBSbMFle1kXVbVtrxV4G3iZ8W4WwZhZBnQjGheKGLeu8nxxXsd0kFSbGOci9ghEtpTqa/1v1R9e9O4Ppva7xwQ2+NI8g2W+D5QQhTSMo1al1wet6kk+XL2F1OXt0/K2/8OvcB9gjwSOyAdQhSE5EaCRVlMkpLwg0VLeatB4+z7fAM2BS9TKH7iJYTNP8a3Y3A92/xjwf000kS5gfZLuYRBSD9gHFWFGWeF1lG38JuApJ78uUe343IaIynU7L4Tea/0OMEz5w0ocFqA4IwQYkFIwz/KQZ2W5iZAKsAN0gWmZZpU8t93/W3fXd1VNUUoqzscT8/oJSSUqpP2eFlIaxV8kak0prmoJtWaaNao7vu4PT79073w+l8vV2tbv9Ja20b6VofmsNx31+Gy+VytTEMw/l8HobLX4kr4YC5V4LvAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/49b43b99192d60e480e03ab0b87f35c6/c54d4/idea-compare-files.webp 175w, /static/49b43b99192d60e480e03ab0b87f35c6/a3432/idea-compare-files.webp 350w, /static/49b43b99192d60e480e03ab0b87f35c6/426ac/idea-compare-files.webp 700w, /static/49b43b99192d60e480e03ab0b87f35c6/c139f/idea-compare-files.webp 1050w, /static/49b43b99192d60e480e03ab0b87f35c6/7f403/idea-compare-files.webp 1400w, /static/49b43b99192d60e480e03ab0b87f35c6/0fbe4/idea-compare-files.webp 2686w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/49b43b99192d60e480e03ab0b87f35c6/4edbd/idea-compare-files.png 175w, /static/49b43b99192d60e480e03ab0b87f35c6/13ae7/idea-compare-files.png 350w, /static/49b43b99192d60e480e03ab0b87f35c6/8c557/idea-compare-files.png 700w, /static/49b43b99192d60e480e03ab0b87f35c6/e996b/idea-compare-files.png 1050w, /static/49b43b99192d60e480e03ab0b87f35c6/2cefc/idea-compare-files.png 1400w, /static/49b43b99192d60e480e03ab0b87f35c6/d185f/idea-compare-files.png 2686w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/49b43b99192d60e480e03ab0b87f35c6/8c557/idea-compare-files.png" alt="IDEA Compare files" title="IDEA Compare files" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Each difference is color-coded:</p> <ul> <li>No coloring means the content is the same</li> <li>Blue means changes on the same line</li> <li>Green means new content</li> <li>Gray means removed content</li> </ul> <p>You can click the arrow icons <code class="language-text">»</code> and <code class="language-text">«</code> to apply a particular change from one file to another.</p> <p>This comparison works for images, too, although you cannot see and apply individual differences.</p> <h2 id="comparing-with-non-project-file" style="position:relative;"><a href="#comparing-with-non-project-file" aria-label="comparing with non project file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Comparing with non-project file</h2> <p>Another case is when you need to compare a file from your project with another file outside of it.</p> <p>The process is again very similar. Select a single file in your project window and:</p> <ol> <li>Right-click one of the files and select <code class="language-text">Compare With...</code></li> <li>Press <kbd>Ctrl</kbd> + <kbd>D</kbd></li> </ol> <p>The last step is to browse for an external file to compare with. Now the comparison is the same as in the example above.</p> <h2 id="comparing-with-the-clipboard" style="position:relative;"><a href="#comparing-with-the-clipboard" aria-label="comparing with the clipboard permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Comparing with the clipboard</h2> <p>Maybe you have a file in your project, and you need to compare it with some external content, which is not saved as a file on your machine. Maybe it is a code snippet from the web, for example, from stack overflow.</p> <p>First, you need to open the file from your project in your editor. Then copy to clipboard the snipped you want to compare (<kbd>Ctrl</kbd>+<kbd>C</kbd>).</p> <p>Now you have two options. Either compare the whole file against the clipboard or just a selection. If you want the whole file to be compared, just right-click anywhere in the editor and select <code class="language-text">Compare with Clipboard</code> from the context menu. If you want just a selection instead, select some fragment of the file first and then right-click as before. </p> <h2 id="custom-comparison" style="position:relative;"><a href="#custom-comparison" aria-label="custom comparison permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Custom comparison</h2> <p>What about the case when you want to compare two non-file code snippets from external sources? You can do this too! Just run <code class="language-text">Find Action</code> via <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> and then search for <code class="language-text">Open Blank Diff Window</code>.</p> <p>This opens a brand new diff window with both panels blank, so you can copy and paste both snippets to be compared.</p> <h2 id="comparing-with-the-previous-local-version-of-the-file" style="position:relative;"><a href="#comparing-with-the-previous-local-version-of-the-file" aria-label="comparing with the previous local version of the file permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Comparing with the previous local version of the file</h2> <p>Even if you're not using any Version Control system, IDEA stores historical versions of your local files. You can right-click in your editor and select <code class="language-text">Local history → Show history</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/6efec4fca600b1f183e8aec4d617581b/1c181/idea-local-history.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 44.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABjElEQVQoz01R247TMBDNzwNPPPEF/AWghV0hoBIPCK3QVhQ2tWOnduL7JXFiJylTBNpalkdzNHPmzHHlJZ+8ykHPQS+DTd5wzilt67qmlHRdRylljCGE+l5AqrUOIcALYPXqDr18f3z25teLt7+fv0Ovd5gzBg2EUCGEMQbqlFKMndqWEUqk0nEYYojWueru4XTzg3/Yi9u9uHkQu0OnlGacG2tTmoZhTGkECuus6ZXppJNmTqnknOepGp2ZYljStKRxnaYUg9PO9sYIG7wHAMZYazphMPE1GhBODfdUKtrrShuTSzn/P8tSuHCPWMYhhzHHsfg4NcwfGo/aRFhG7YzZQIVvRaxgn5KfmkspSgXGQy9iJwbtJmnTAbufjUbkBEZi3NYYfANxtoJ95nm+as6yN4T0GHcNVUJbYfyxld/Z/af62+3+Hu7H49cvp887tquklOVKNhBNT1zb+bxu6wI+gOtzLtt2XleAIG5lKZWzDpy7FG6X8nEc4Sf+pts/gm3z3oPA6xmXMXn+A2799XqkVpXZAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/6efec4fca600b1f183e8aec4d617581b/c54d4/idea-local-history.webp 175w, /static/6efec4fca600b1f183e8aec4d617581b/a3432/idea-local-history.webp 350w, /static/6efec4fca600b1f183e8aec4d617581b/426ac/idea-local-history.webp 700w, /static/6efec4fca600b1f183e8aec4d617581b/c139f/idea-local-history.webp 1050w, /static/6efec4fca600b1f183e8aec4d617581b/7f403/idea-local-history.webp 1400w, /static/6efec4fca600b1f183e8aec4d617581b/b42b7/idea-local-history.webp 2696w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/6efec4fca600b1f183e8aec4d617581b/4edbd/idea-local-history.png 175w, /static/6efec4fca600b1f183e8aec4d617581b/13ae7/idea-local-history.png 350w, /static/6efec4fca600b1f183e8aec4d617581b/8c557/idea-local-history.png 700w, /static/6efec4fca600b1f183e8aec4d617581b/e996b/idea-local-history.png 1050w, /static/6efec4fca600b1f183e8aec4d617581b/2cefc/idea-local-history.png 1400w, /static/6efec4fca600b1f183e8aec4d617581b/1c181/idea-local-history.png 2696w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/6efec4fca600b1f183e8aec4d617581b/8c557/idea-local-history.png" alt="Local History" title="Local History" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Here you can browse older versions of your current file and see the difference between the old and the current version and apply any changes if required.</p> <h2 id="compare-with-vcs" style="position:relative;"><a href="#compare-with-vcs" aria-label="compare with vcs permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Compare with VCS</h2> <p>If you're using a Version Control System, you have several more comparison options. For example, if you're using Git, you can go to <code class="language-text">VCS → Git</code> or right click your editor and select <code class="language-text">Git</code>. Now you can:</p> <ul> <li><strong>Compare with the same repository version</strong>: Compares the current local file with the version in your remote repository </li> <li><strong>Compare with branch</strong>: Compares the local file with the same file in a different branch</li> <li><strong>Show history</strong>: Compares the local file with its previous versions</li> </ul> <h2 id="comparing-folders" style="position:relative;"><a href="#comparing-folders" aria-label="comparing folders permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Comparing Folders</h2> <p>The comparison works not only for individual files but also for the whole directories. The process is the same as for files - just select two folders in your Project window and press <kbd>Ctrl</kbd> + <kbd>D</kbd> or right click and <code class="language-text">Compare Directories</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/d50b04eebaa279ab7b3ec7c0c1f96604/b0f48/idea-compare-directories.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 47.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABsUlEQVQoz02NSXObQBCF+f+/I7kkkiulU3KS4zgpBFoAKVoQiGHWHoZdtgQYkJErh7z+quv1665qjXNOKEUBCkPEOMMY+77veR5lbBillNFQH4rjOM/yAaUipVSeFxrct0oIKQSAlFwApXTww5Rm2T25WwkAjHOIpJDAhOAASZJq1+qlfjv3t/b2n97a6lIVbVdXzaXr6lvftW1dN9eur7qu+ujXvm+1kcU+L8JPczp24rF958FJvlj8q01HFp+s1WQTj+z42zqabNTYjkbWv7MBLYQkhDTg8ZGAx6IBl8ARg4fBp3IboD2mQ3gg1GXcpTyABEFyEvEQarGKkzRnXCFEQswpBRQyQjghDBMxJGVRFsVLkp6L/DXLXpO0TLOzUhkIoa1mm9VeTHX0e0mOuERQnfiVRg1Pe9NtTLeyd9Rc40cD/1oFtgcnfrnDLkQ12l/HX27lnyWbGnS5T3ZBvg1yn55x1Oi7Wt9Vzl4s1ly35A/jOLXcQ1juUeHiEstGW+6YaXtP5mlqoO/P/vMimNnDK2YdEvNQzQ9XaxvOVv6j4U91/NPAM4fOHDbfMH0t3gFCo907zKrE7AAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/d50b04eebaa279ab7b3ec7c0c1f96604/c54d4/idea-compare-directories.webp 175w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/a3432/idea-compare-directories.webp 350w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/426ac/idea-compare-directories.webp 700w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/c139f/idea-compare-directories.webp 1050w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/7f403/idea-compare-directories.webp 1400w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/205ae/idea-compare-directories.webp 2690w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/d50b04eebaa279ab7b3ec7c0c1f96604/4edbd/idea-compare-directories.png 175w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/13ae7/idea-compare-directories.png 350w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/8c557/idea-compare-directories.png 700w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/e996b/idea-compare-directories.png 1050w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/2cefc/idea-compare-directories.png 1400w, /static/d50b04eebaa279ab7b3ec7c0c1f96604/b0f48/idea-compare-directories.png 2690w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/d50b04eebaa279ab7b3ec7c0c1f96604/8c557/idea-compare-directories.png" alt="Compare directories" title="Compare directories" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p> <p>Here you can see a list of all the files present in both or in either one of the directories. You can easily spot which files are present in just one folder and which in both. These in both, you can compare as usual.</p> <h3 id="synchronizing-folders" style="position:relative;"><a href="#synchronizing-folders" aria-label="synchronizing folders permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Synchronizing folders</h3> <p>The directory diff tool is useful not only for spotting differences in both directories, but also for synchronizing changes. You can apply changes for individual sections of each file as usual. But you can also mark files present only in one of the directories to be either kept or synchronized to the other directory. You can change the desired action for each file in the <code class="language-text">*</code> column. Once you are satisfied, you can hit either <code class="language-text">Synchronize selected</code> or <code class="language-text">Synchronize all</code>.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <a class="gatsby-resp-image-link" href="/static/845d8507b88c70f43e8ccac72f6973e6/b0f48/idea-synchronize-directories.png" style="display: block" target="_blank" rel="noopener" > <span class="gatsby-resp-image-background-image" style="padding-bottom: 47.42857142857142%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAACToAAAk6AGCYwUcAAABc0lEQVQoz12RW2vcMBBG9f//RR4aQinZ9iXvfSgUSkO6aZbG8UWy5ZtG0sh3O2t7MzXZ0uRwJD6G70GMWJbnMpWC8zgWWZYlSRJGURiGyZMnOVcA6owxxm2ABq11VdXManAGDJQWSgp0Q5FpVWopG1WgKqxWaACNoo6zr4GGTeWYX5hQY4JdiqPEQeIYm57rytfIpXrkaaBrodsAnJfbQDsf0Ff4pCw3Lbv6mX3Zq5uD2e319ebu3lz/Kj/dqd238Oqrf3mrPu/1hx/Zxff0422x1WBrApunfpmGeezWqTs9DySF49BMfTehqSHtW5yHtm1M29ihx7+d46vs/uHh9+Hwx/M8P+Ai3kwiEQsKcUJGXIRc0FzEiYgl5SCKgog/BhGj9ZZlSduz1iIiHUPrQa0RsMLuP3ADAPI8L4qCvonRtKVHnanqyjl0RnZ9M07jssynjXVd/9XaM6x+S1W5aazn53qZe3I+dqd1JNdlbJp33foF4rLsnUAdt4AAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/845d8507b88c70f43e8ccac72f6973e6/c54d4/idea-synchronize-directories.webp 175w, /static/845d8507b88c70f43e8ccac72f6973e6/a3432/idea-synchronize-directories.webp 350w, /static/845d8507b88c70f43e8ccac72f6973e6/426ac/idea-synchronize-directories.webp 700w, /static/845d8507b88c70f43e8ccac72f6973e6/c139f/idea-synchronize-directories.webp 1050w, /static/845d8507b88c70f43e8ccac72f6973e6/7f403/idea-synchronize-directories.webp 1400w, /static/845d8507b88c70f43e8ccac72f6973e6/205ae/idea-synchronize-directories.webp 2690w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/845d8507b88c70f43e8ccac72f6973e6/4edbd/idea-synchronize-directories.png 175w, /static/845d8507b88c70f43e8ccac72f6973e6/13ae7/idea-synchronize-directories.png 350w, /static/845d8507b88c70f43e8ccac72f6973e6/8c557/idea-synchronize-directories.png 700w, /static/845d8507b88c70f43e8ccac72f6973e6/e996b/idea-synchronize-directories.png 1050w, /static/845d8507b88c70f43e8ccac72f6973e6/2cefc/idea-synchronize-directories.png 1400w, /static/845d8507b88c70f43e8ccac72f6973e6/b0f48/idea-synchronize-directories.png 2690w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/845d8507b88c70f43e8ccac72f6973e6/8c557/idea-synchronize-directories.png" alt="Synchronize Folders" title="Synchronize Folders" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </a> </span></p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[Netlify Analytics Review: Google Analytics alternative?]]>https://www.vojtechruzicka.com/netlify-analytics/https://www.vojtechruzicka.com/netlify-analytics/Fri, 11 Oct 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 55.99999999999999%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABQAE/8QAFQEBAQAAAAAAAAAAAAAAAAAABAD/2gAMAwEAAhADEAAAAR0jV0G1W2j/AP/EABoQAAICAwAAAAAAAAAAAAAAAAACAQMSISL/2gAIAQEAAQUCWBF3gKUCxz//xAAYEQACAwAAAAAAAAAAAAAAAAAAAQMREv/aAAgBAwEBPwGZ0YZ//8QAFxEBAAMAAAAAAAAAAAAAAAAAAAISMf/aAAgBAgEBPwGGLv/EABYQAQEBAAAAAAAAAAAAAAAAAAABIP/aAAgBAQAGPwJMR//EABoQAAIDAQEAAAAAAAAAAAAAAAABESExQVH/2gAIAQEAAT8hn2kyHgtpCSMOmW3wRgf/2gAMAwEAAgADAAAAEHgP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAwEBPxBByGN2/8QAFhEBAQEAAAAAAAAAAAAAAAAAAQAR/9oACAECAQE/EDmpY3//xAAbEAEBAAIDAQAAAAAAAAAAAAABEQAhMUFhkf/aAAgBAQABPxAmUI17ghOpak+5CUV3m7soc3L5Fg79ykipXP/Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/377e018b5f79085a167ec4e892985830/c54d4/netlify-analytics.webp 175w, /static/377e018b5f79085a167ec4e892985830/a3432/netlify-analytics.webp 350w, /static/377e018b5f79085a167ec4e892985830/426ac/netlify-analytics.webp 700w, /static/377e018b5f79085a167ec4e892985830/c139f/netlify-analytics.webp 1050w, /static/377e018b5f79085a167ec4e892985830/7f403/netlify-analytics.webp 1400w, /static/377e018b5f79085a167ec4e892985830/fad48/netlify-analytics.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/377e018b5f79085a167ec4e892985830/e52aa/netlify-analytics.jpg 175w, /static/377e018b5f79085a167ec4e892985830/70ebb/netlify-analytics.jpg 350w, /static/377e018b5f79085a167ec4e892985830/29d31/netlify-analytics.jpg 700w, /static/377e018b5f79085a167ec4e892985830/9ecec/netlify-analytics.jpg 1050w, /static/377e018b5f79085a167ec4e892985830/d165a/netlify-analytics.jpg 1400w, /static/377e018b5f79085a167ec4e892985830/b17f8/netlify-analytics.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/377e018b5f79085a167ec4e892985830/29d31/netlify-analytics.jpg" alt="Netlify Analytics" title="Netlify Analytics" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Zero-impact server-side analytics for your Netlify projects. Works even with adblockers.</p> <h2 id="netlify-analytics" style="position:relative;"><a href="#netlify-analytics" aria-label="netlify analytics permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Netlify Analytics</h2> <p>Netlify is a cool service focused on building and deploying your static and <a href="https://www.vojtechruzicka.com/gatsby-migration/">JAM Stack</a> sites. If you're interested you can read more about the service in one of my previous posts:</p> <div class="linked-article"><h4 class="front-post-title" style="margin-bottom: 0.375rem;"><a href="/jamstack-migration-netlify/" style="box-shadow: none;">Migration to JAM stack and Netlify from WordPress</a></h4><small class="front-post-info"><span class="front-post-info-date">21 May, 2018</span><div class="post-tags"><ul><li><a href="/tags/blogging/">#Blogging</a></li><li><a href="/tags/jam-stack/">#JAMStack</a></li></ul></div></small><div><a class="front-post-image" href="/jamstack-migration-netlify/"><div class=" gatsby-image-wrapper" style="position: relative; overflow: hidden;"><div style="width: 100%; padding-bottom: 66.6667%;"></div><img src="data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFA//EABYBAQEBAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAABdxcMyOUQv//EABkQAAIDAQAAAAAAAAAAAAAAAAABAhESIf/aAAgBAQABBQJ1U0ZK5lEl3//EABYRAQEBAAAAAAAAAAAAAAAAAAASE//aAAgBAwEBPwGGb//EABcRAQADAAAAAAAAAAAAAAAAAAABAxP/2gAIAQIBAT8Bmxs//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAERIDH/2gAIAQEABj8CHBlP/8QAGBABAQEBAQAAAAAAAAAAAAAAAREAITH/2gAIAQEAAT8hkVFFnDSADU+jSLMPLO4Wt//aAAwDAQACAAMAAAAQ7B//xAAWEQEBAQAAAAAAAAAAAAAAAAABABH/2gAIAQMBAT8QQmQMv//EABYRAQEBAAAAAAAAAAAAAAAAAAEQIf/aAAgBAgEBPxATY//EABoQAQEAAwEBAAAAAAAAAAAAAAERACExQWH/2gAIAQEAAT8QGr3lt/cFhPBDmPvRruLMUpdcwLohFzRkpYZ//9k=" alt="" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 0;"><picture><source srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" sizes="(max-width: 180px) 100vw, 180px"><img sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" src="/linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg" alt="" loading="lazy" style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; object-fit: cover; object-position: center center; opacity: 1; transition: none 0s ease 0s;"></picture><noscript><picture><source srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" sizes="(max-width: 180px) 100vw, 180px" /><img loading="lazy" sizes="(max-width: 180px) 100vw, 180px" srcset="/linked/jamstack-migration-netlify/5e4a3/jamstack-migration-netlify.jpg 45w, /linked/jamstack-migration-netlify/e451c/jamstack-migration-netlify.jpg 90w, /linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg 180w, /linked/jamstack-migration-netlify/b3ebb/jamstack-migration-netlify.jpg 270w, /linked/jamstack-migration-netlify/8841e/jamstack-migration-netlify.jpg 360w, /linked/jamstack-migration-netlify/2b1a3/jamstack-migration-netlify.jpg 900w" src="/linked/jamstack-migration-netlify/29fd0/jamstack-migration-netlify.jpg" alt="" style="position:absolute;top:0;left:0;opacity:1;width:100%;height:100%;object-fit:cover;object-position:center"/></picture></noscript></div></a><span class="front-post-excerpt">How and Why I migrated from WordPress to static JAM Stack site deployed on Netlify.</span></div></div> <p>One of the new features recently <a href="https://www.netlify.com/blog/2019/07/10/netlify-analytics-accurate-insights-without-performance-impacts/">announced by Netlify</a> is <a href="https://www.netlify.com/products/analytics/">Netlify Analytics</a>. In its purpose, it is similar to Google Analytics - gather stats about people visiting your pages. Unique visitors, pageviews, that sort of stuff.</p> <p>Unlike <a href="https://analytics.google.com/analytics/">Google Analytics</a>, it does not rely on JavaScript to gather and send this data. Instead, everything happens on the Netlify servers. That has some advantages we'll discuss in a moment. Now let's see how the Netlify Analytics dashboard looks like.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 230.2857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAuCAYAAAAoaDnGAAAACXBIWXMAAAsSAAALEgHS3X78AAAFyklEQVRIx51XS29VVRQ+CCSKIoE+pLRQeSiP1Jr4ICQ6caBRpkYx6MyBA4cmDjSEGhIFIf4WBybOdGJiRETFxjaNhT4strT33PN+n7tc39p7n3vOKTWGk3x3v9b+9tprrb32vtbAxPM0MvkijRx/hkZOTtLw0xN0cOI5OnH6JRqffIH2Hj5eYffBo4LHDx2rsBvgvvFTz9Lk6TNk7Ro7StsGR+mhgRHatg/YT9sHDtDO4THaMTRG1t4nBNsHD9DgsZOMU7Rn/Cnaw0RSagwcOUHDPG6defUsfXblGl34/IqUwNTla3Txiy/p4uWrNIU249LVr2jy5Vfo4f2HRKPHxo408OjoYdrFsD66MEX/9zv3/gei+RBruY81uh+s+YUluru6ylijLMtobX2dFpaWKUlS8oNA6na3S0VR0Oxft+n6r7foxu/TW8LK8pI2Oh2ZVJYlRXFMrueJRnmeSx0L4cuKkhKWT/8Dlh/FQuL5Abm+TzlPKsseZUyWs1aoo0TbD2Ny/LCCG0QNoM8K4oQ6dlcIy16Pt1YKAYgLQaHKUqEs+wsmadpAGKdkRWwr2AvbIybsMerfVm2UpV6gB/TYXIkmBJkI1CbfiUPysAh/6M+hoR4vW7KmXhGWvK2VJKZbgUf/pAnNM9kPrk03fYdW0pj+DH36LXBpOvRomeVmIl/65qKAFpNI5nXylNI0U4SwUydLaYkHgWUGiEGGCX8zCeooF2Vc1Ze1PLDO8+OECfGD7ebaq4U4pI92W/q0gwqJgn49glOCKKE0g8cyAeppZso6Nvcp+T7AZYUcNo7rku04FHJMisHLnvZiv24gfb3NYQP7RSZsoC61wqNoeTGrebkVR1W1HzbwMht9lR2BD0b+ztmgP9ir0OoGe/tb+x795HWFeIY9jDHMwWLwdlAoEygNWX0IXfe7tJYlEgY/M8kvjIU44vBxpY7Quc0hBbkfPVtIsThCDGUVNvCUz6R2non7EUJdrpu2res219f1mMOA3IYed1nDfmAzYZ4XEjqFLpttBQkTc7aLsuoTsCwcLGETSsZJxHPA/cID42Ec6zKhGJ5tyemw4S3adpVEoRU0bicEPwglK9ldR+Q9z9/kcDkpWA1xBLJMB6iQ6rhLEtYmUbEWs4apTra9WmaCLMZCc1KQ6sMoEgRhKAm30EcM9SBUYx4nYBBnmTFNJp6FIjKXTac0hBaxQhSrDB5zH1YGQSCLxWIWbHft3jp15WRFogxkozoh7g0cP2gnW0u0GVhDtXosMDKpOCRTyQKRwCXmVITYCkghrFaLZXUIZ3LolYbQCnLQyuHS1YBJkjqh43qSHHxtv5AFoAG2nNY0hAy07DqukBsTiG3bGkIjTG54mrcshOJBrSHLglQRcwgxqZhq85aVB6uEWSovg1C01lvGdj2GlNpUGG8QorPLK8J+5jYzV2Za8zIIjcNMJAAInU2EAO5mE4tiw9aWYUPA2BAl2tC08nKgt+xob5mAlYNvCCNFqBZV22x6OWx7Wa2EuKpf4GUPhGrLALyqNFTOkJK9DFJ1AOL+JdXMMs3LJxK7ZVU2SrgP2Uagz3liLin8IIPAHhhoXJX6TYN3DqLg3kZHMg5SWP29Y1C9bcz5lRdXK33BBBCG0UMEfhWveeOdU11SsCE8K2FSC5miUHaEweEstOsPJ1moBrQrQjgFgVqlcw0Qwns4khl7nbTmWSsBmy80hObAV97VMNkG3uMGrfANiJvv684qfc/XLG47tHG93uUrOE9zFYc4JTC6sSVMIElWLq+cPNaQ7UE3eeKni7N0aWmOPr4zQ58szNLU4hx9OD9N33TWqARhPX0l+lglaSJHrtSPIHmiyNM4J4dfXT7LeTAFgpnjFPUoz5QNETYmnmAng7QWlxDEuJwgxGwLpk/i0OOHuEH9AV7vb8hsATNuvf7WeXrj7Xfp7Ln3FLiO9mtvvvNAsPDP6JGRJ+Uf0s6hUcEOBv7bPQj+BRr483RQDFzJAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/23574b362012d207fdea20caf8f99612/c54d4/netlify-analytics-example.webp 175w, /static/23574b362012d207fdea20caf8f99612/a3432/netlify-analytics-example.webp 350w, /static/23574b362012d207fdea20caf8f99612/426ac/netlify-analytics-example.webp 700w, /static/23574b362012d207fdea20caf8f99612/c139f/netlify-analytics-example.webp 1050w, /static/23574b362012d207fdea20caf8f99612/7f403/netlify-analytics-example.webp 1400w, /static/23574b362012d207fdea20caf8f99612/919bf/netlify-analytics-example.webp 3200w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/23574b362012d207fdea20caf8f99612/4edbd/netlify-analytics-example.png 175w, /static/23574b362012d207fdea20caf8f99612/13ae7/netlify-analytics-example.png 350w, /static/23574b362012d207fdea20caf8f99612/8c557/netlify-analytics-example.png 700w, /static/23574b362012d207fdea20caf8f99612/e996b/netlify-analytics-example.png 1050w, /static/23574b362012d207fdea20caf8f99612/2cefc/netlify-analytics-example.png 1400w, /static/23574b362012d207fdea20caf8f99612/dcef9/netlify-analytics-example.png 3200w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/23574b362012d207fdea20caf8f99612/8c557/netlify-analytics-example.png" alt="Netlify Analytics in Action" title="Netlify Analytics in Action" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>That's it: just this one screen, no other sub-views per category of per various metrics. Netlify's philosophy here is not to overwhelm users with a lot of data and keep it simple. Still, the product is new, and there are more features to come. It will never be as feature-rich as GA, though.</p> <h2 id="advantages" style="position:relative;"><a href="#advantages" aria-label="advantages permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Advantages</h2> <p>Unlike Google Analytics and other similar services, Netlify Analytics runs on the server-side, which has some nice implications.</p> <h3 id="better-performance" style="position:relative;"><a href="#better-performance" aria-label="better performance permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Better performance</h3> <p>Since everything happens server-side, there is zero impact on client performance. You don't need to include any external JavaScript, parse and execute it and make any calls to an external service. Google Analytics script currently has 43.4 kb (although it is much smaller compressed). It may not seem as much, but it can make a difference on low-end devices and with poor network connectivity.</p> <h3 id="works-with-adblockers" style="position:relative;"><a href="#works-with-adblockers" aria-label="works with adblockers permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Works with adblockers</h3> <p>Adblockers are really popular these days. What's problematic, is that most of them also block analytics tools. You lose information on all of your visitors, who use adblockers. According to <a href="https://www.statista.com/statistics/351862/adblocking-usage/">the following report</a> from 2018, globally 27% of desktop users on average use adblockers. But that's just average. It goes as high as 42% in Greece! That's huge. It means that a huge portion of your traffic can go untracked. It can get even worse, depending on your target audience. Likely, more tech-savvy users use adblockers more often. If you have a developer-targeted blog like me, the percentage is going to be way higher than the broad average. </p> <h3 id="works-with-javascript-disabled" style="position:relative;"><a href="#works-with-javascript-disabled" aria-label="works with javascript disabled permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Works with Javascript disabled</h3> <p>Since Netlify Analytics doesn't rely on Javascript, it works even if a client has JavaScript disabled. In the era of React, Angular, and other JS frameworks everywhere, the percentage is very low these days. Still, it may be useful to capture these visitors as well (assuming your page can actually run without JS).</p> <h3 id="historical-data-when-enabled" style="position:relative;"><a href="#historical-data-when-enabled" aria-label="historical data when enabled permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Historical data when enabled</h3> <p>The good news is that Netlify already collects analytics data for you, even if you don't have analytics enabled. You just have to pay to view it. Once you do, you'll be able to view historical data up to 30 days back. It may take some time to import everything, though, so don't expect to see everything immediately.</p> <h3 id="error--not-found-pages" style="position:relative;"><a href="#error--not-found-pages" aria-label="error not found pages permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Error &#x26; Not Found pages</h3> <p>By its nature, JS-powered tools such as Google Analytics, can show you only valid pages. Since Netlify Analytics is server-side, it can also capture requests to non-existent pages and resources. This may be handy to detect broken links and other ismilar issues.</p> <h3 id="simplicity" style="position:relative;"><a href="#simplicity" aria-label="simplicity permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Simplicity</h3> <p>GA is a very powerful tool, but it may be intimidating at first, if you're not familiar with it. For casual usage, it may be too complex. Netlify analytics is very simple and easy to understand, which is good.</p> <h2 id="disadvantages" style="position:relative;"><a href="#disadvantages" aria-label="disadvantages permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Disadvantages</h2> <p>Although Netlify Analytics has some powerful features, it has, currently, also some serious flaws. This is to be expected as the project is still fresh and not mature yet. According to Netlify, there are fixes and enhancement to many of the disadvantages currently on the roadmap.</p> <h3 id="accuracy" style="position:relative;"><a href="#accuracy" aria-label="accuracy permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Accuracy</h3> <p>Netlify Analytics promises improved accuracy because it can track even users without javascript, those who have adblockers, and even missing resources. It's true to some degree. The problem is that it currently also tracks visits from bots, which can greatly affect your stats and provide irrelevant data.</p> <p>Let's compare my stats on Netlify vs Google Analytics to illustrate this:</p> <ul> <li>Unique visitors: 44K vs 50K ⟶ + 13.6%</li> <li>Page views: 57K vs 439K ⟶ + 770 %</li> </ul> <p>The Netlify data is supposed to be higher as it also detects users with adblockers, but this looks like way too much and is likely because of the bot activity.</p> <p>Fortunately, according to Netlify, it is something they are already working on, and hopefully, it will be fixed soon. Currently, however, your stats include a lot of irrelevant data from bot visits.</p> <h3 id="limited-date-range" style="position:relative;"><a href="#limited-date-range" aria-label="limited date range permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Limited date range</h3> <p>One of the most significant shortcomings is a limited history of your data. Currently, you can see only the last 30 days of user activity. You don't have any means of determining how is your traffic changing in the a long term. Did all these new articles bring enough traffic, or is it stagnant? How did your traffic rise over the past few years?</p> <p>According to Netlify, the date range is likely to be increased in the future, but is it ever going to full history, which GA already offers? Is it much better to have 180 days instead of 30 if your site is running for years?</p> <h3 id="limited-functionality" style="position:relative;"><a href="#limited-functionality" aria-label="limited functionality permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Limited functionality</h3> <p>The feature set of Netlify Analytics is so far very limited; it's just one screen, which we saw in the image above. That's it. And it's not likely to be anywhere near to what GA offers anytime soon. Or ever. There a list of features which are currently offered:</p> <ul> <li>Total pageviews</li> <li>Total unique visitors</li> <li>Total bandwidth used</li> <li>Pageviews over the last 30 days: chart</li> <li>Unique visitors over the last 30 days: chart</li> <li>Top pages by pageviews</li> <li>Top resources not found by the number of requests</li> <li>Top sources of traffic by pageviews</li> <li>Bandwidth used over the last 30 days: chart </li> </ul> <p>If it is good enough for your case, that's great. However, it's likely you need more. There's not drill down per page, region, or device. No bounce rate, time spent on-page, or session flow. No demographics about users. Not to mention custom metrics.</p> <h3 id="price" style="position:relative;"><a href="#price" aria-label="price permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Price</h3> <p>The biggest downside is pricing. Adding analytics costs $9 per month PER SITE. This can quickly ramp up if you're running multiple different sites on Netlify. The price seems quite steep, considering the biggest competitor, Google Analytics, has many more features and is completely free. I can imagine running and developing such kind of service can cost a lot of money. However, for a user, there needs to be a strong justification to choose a $9 service with fewer features over a free, more powerful alternative. It doesn't mean Netlify analytics is bad, but the alternatives may be more compelling considering the cost.</p> <p>Also, the $9 price is only for pages with less than 250K monthly pageviews. If you have more, they have <a href="https://www.netlify.com/pricing/#analytics">custom pricing</a>, and you need to contact their sales team.</p> <h2 id="conclusion" style="position:relative;"><a href="#conclusion" aria-label="conclusion permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Conclusion</h2> <p>Netlify Analytics is a nice service, which has some powerful features, because of the fact, that it is running completely server-side. That means better performance and tracking of a portion of your traffic, which would otherwise not be visible. The setup is effortless and straightforward, and it's nicely integrated with Netlify.</p> <p>It is still a new product and I'm expecting it to evolve and mature over time. Hopefully, some pain-points, such as bot tracking, will be fixed soon, and the service will be even better.</p> <p>As for the price, it may not be justifiable on its own, but you have to take into consideration that Netlify provides a cool service with a generous free plan, and this is a way to make some profit by an optional product. Even though analytics are not perfect yet, I expect it to mature, and I'll be happy to pay $9 as a way to support a company offering a great product (Netlify as a whole), while still using the free plan.</p> <p>If you're not too keen on supporting Netlify, it may be a better approach to wait some time until the product is more mature.</p> <p>As for now, I'll still be using Netlify Analytics. However, I cannot really ditch GA either as I also want to have historical data. This way, I lose the performance benefit, and the only real remaining advantage is tracking users with adblockers and disabled JavaScript.</p>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[JavaFX Tutorial: CSS Styling]]>https://www.vojtechruzicka.com/javafx-css/https://www.vojtechruzicka.com/javafx-css/Thu, 10 Oct 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAAB4M6iViE//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAQABBQJ1qaf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFX/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/AWV//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGRAAAwEBAQAAAAAAAAAAAAAAAAERMRAh/9oACAEBAAE/IWbKU8E7qpN4/9oADAMBAAIAAwAAABC4D//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/EEP/xAAWEQEBAQAAAAAAAAAAAAAAAAAAAWH/2gAIAQIBAT8QkZP/xAAaEAEBAQEBAQEAAAAAAAAAAAABEQAxQVEh/9oACAEBAAE/ELJSES+uQdBnw01/YeaqeDjJvwDhcm//2Q=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/3fc851f8b5da1ae3cdb8fca5932fb112/c54d4/css.webp 175w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/a3432/css.webp 350w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/426ac/css.webp 700w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/c139f/css.webp 1050w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/7f403/css.webp 1400w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/fad48/css.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/3fc851f8b5da1ae3cdb8fca5932fb112/e52aa/css.jpg 175w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/70ebb/css.jpg 350w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/29d31/css.jpg 700w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/9ecec/css.jpg 1050w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/d165a/css.jpg 1400w, /static/3fc851f8b5da1ae3cdb8fca5932fb112/b17f8/css.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/3fc851f8b5da1ae3cdb8fca5932fb112/29d31/css.jpg" alt="CSS In JavaFX" title="CSS In JavaFX" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>How to style JavaFX components using good old CSS.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li><a href="/javafx-layouts-advanced/">JavaFX Tutorial: Advanced layouts</a></li> <li class="series-current">JavaFX Tutorial: CSS Styling</li> <li><a href="/javafx-spring-boot/">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</a></li> </ol> </div> <h2 id="separating-visuals" style="position:relative;"><a href="#separating-visuals" aria-label="separating visuals permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Separating visuals</h2> <p>In the previous <a href="/javafx-fxml-scene-builder/">article about FXML</a>, we learned how JavaFX achieves clean separation of concerns by dividing the user interface code into two parts. Components and their properties are declared in an FXML file, while the interaction logic is neatly separated in a controller.</p> <p>Now there is a third part on top of this. FXML manages only what components are in your app, their properties, and how they are nested. It does not define the visuals of the component, though. That is - fonts, colors, backgrounds, paddings. To be honest, you can achieve it in FXML, but you shouldn't. Instead, visuals should be clearly separated in CSS stylesheets.</p> <p>This way, your styling is independent and can be easily replaced or changed without affecting the rest of the application. You can easily even have multiple themes, which you can switch on demand.</p> <h2 id="css" style="position:relative;"><a href="#css" aria-label="css permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS</h2> <p>You probably know CSS (Cascading Style Sheets) from the web, where it is used to style HTML pages. In JavaFX, this is very similar, although JavaFX uses a set of its own custom properties.</p> <p>Let's see an example:</p> <div class="gatsby-code-button-container" data-toaster-id="82468061489457040000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.button { -fx-font-size: 15px; }`, `82468061489457040000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.button</span> <span class="token punctuation">{</span> <span class="token property">-fx-font-size</span><span class="token punctuation">:</span> 15px<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>There are two essential concepts here. The first one is the selector. That's <code class="language-text">.button</code>. This determines to which components the styling should be applied. There it is for ALL the buttons.</p> <p>The second part is actual styling properties, which will be applied to all the components, which match our selector. Properties are everything inside the curly braces.</p> <p>Each property has a specific value. In our example, we have a property <code class="language-text">-fx-font-size</code>, which means how big the text will be. The value is 15px here but could be anything else we would desire.</p> <p>To sum it up - we created a rule which says - all the buttons everywhere should have their text of size 15px.</p> <h2 id="selectors" style="position:relative;"><a href="#selectors" aria-label="selectors permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Selectors</h2> <p>Now let's take a closer look at how selectors work in JavaFX. It pretty much the same as in regular CSS.</p> <h3 id="class" style="position:relative;"><a href="#class" aria-label="class permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Class</h3> <p>Class in CSS represents multiple similar elements. For example, buttons or checkboxes. A selector, which should apply to all of the elements of the same class starts with a dot <code class="language-text">.</code> followed directly by the class name. The convention is to separate individual words with a comma <code class="language-text">-</code>. The following selector applies to all the elements with class <code class="language-text">label</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="71790270324138250000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.label { // Some properties }`, `71790270324138250000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.label</span> <span class="token punctuation">{</span> // Some properties <span class="token punctuation">}</span></code></pre></div> <h4 id="built-in-classes" style="position:relative;"><a href="#built-in-classes" aria-label="built in classes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Built-in classes</h4> <p>The good news is that all the built-in JavaFX components (such as Label or Button) have already a class assigned out of the box. If you want to target all the labels in your app, you don't have to add any custom classes to each of your labels. Each Label has by default <code class="language-text">label</code> class.</p> <p>It is easy to determine the class name from the component.</p> <ul> <li>Take the name of the Java class of the component - eg. Label</li> <li>Make the name lower-case</li> <li>If it consists of multiple words, separate them by <code class="language-text">-</code></li> </ul> <p>Some examples:</p> <ul> <li>Label → label</li> <li>CheckBox → check-box</li> </ul> <p>When using such classes as selectors, don' forget to add <code class="language-text">.</code>. That means the selector for the <code class="language-text">label</code> class is <code class="language-text">.label</code>.</p> <h4 id="custom-classes" style="position:relative;"><a href="#custom-classes" aria-label="custom classes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Custom classes</h4> <p>If build-in classes are not enough, you can add your own custom classes to your components. You can have multiple classes separated by a comma:</p> <div class="gatsby-code-button-container" data-toaster-id="95998119532635460000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<Label styleClass=&quot;my-label,other-class&quot;>I am a simple label</Label>`, `95998119532635460000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name">styleClass</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-label,other-class<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am a simple label<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span></code></pre></div> <p>Or in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="33061813174393962000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Label label = new Label(&quot;I am a simple label&quot;); label.getStyleClass().addAll(&quot;my-label&quot;, &quot;other-class&quot;);`, `33061813174393962000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Label</span> label <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"I am a simple label"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> label<span class="token punctuation">.</span><span class="token function">getStyleClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token string">"my-label"</span><span class="token punctuation">,</span> <span class="token string">"other-class"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Adding classes this way does not remove the default class of the component (<code class="language-text">label</code> in this case).</p> <p>There is one special class called <code class="language-text">root</code>. It means the root component of your scene. You can use it to style everything inside your scene (such as setting a global font). It is similar to using body tag selector in HTML.</p> <h3 id="id" style="position:relative;"><a href="#id" aria-label="id permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>ID</h3> <p>Another way of selecting components in CSS is to use the component's ID. It is a unique identifier of a component. Unlike classes, which can be assigned to multiple components, ID should be unique in a scene.</p> <p>While classes are using <code class="language-text">.</code> before the name in their selectors, IDs are marked with <code class="language-text">#</code>.</p> <div class="gatsby-code-button-container" data-toaster-id="58435060547400550000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`#my-component { ... }`, `58435060547400550000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">#my-component</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre></div> <p>In FXML, you can use <code class="language-text">fx:id</code> to set the component's CSS id.</p> <div class="gatsby-code-button-container" data-toaster-id="93991603501213470000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<Label fx:id=&quot;foo&quot;>I am a simple label</Label>`, `93991603501213470000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name"><span class="token namespace">fx:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>foo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am a simple label<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span></code></pre></div> <p>There is one caveat, though. <a href="/javafx-fxml-scene-builder/#injecting-components-to-controller">This same ID is used to link to a component object declared in your controller with the same name</a>. Since the id and the name of the field in controller need to match, <code class="language-text">fx:id</code> needs to respect Java's naming restriction for field names. Even though the CSS naming convention dictates individual words separated by <code class="language-text">-</code>, it is an invalid character for Java field names. For <code class="language-text">fx:id</code> with multiple words, you need, therefore, to use a different naming convention such as CamelCase or use underscores.</p> <div class="gatsby-code-button-container" data-toaster-id="47829304564767640000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<!-- This is not valid --> <Label fx:id=&quot;my-label&quot;>I am a simple label</Label> <!-- This is valid --> <Label fx:id=&quot;my_label&quot;>I am a simple label</Label> <Label fx:id=&quot;MyLabel&quot;>I am a simple label</Label>`, `47829304564767640000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token comment">&lt;!-- This is not valid --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name"><span class="token namespace">fx:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am a simple label<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- This is valid --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name"><span class="token namespace">fx:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my_label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am a simple label<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name"><span class="token namespace">fx:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>MyLabel<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am a simple label<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java, you can just call the <code class="language-text">setId()</code> method on your component.</p> <div class="gatsby-code-button-container" data-toaster-id="49035871802700505000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Label label = new Label(&quot;I am a simple label&quot;); label.setId(&quot;foo&quot;);`, `49035871802700505000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Label</span> label <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"I am a simple label"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> label<span class="token punctuation">.</span><span class="token function">setId</span><span class="token punctuation">(</span><span class="token string">"foo"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="properties" style="position:relative;"><a href="#properties" aria-label="properties permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Properties</h2> <p>Although CSS used in JavaFX is very similar to the original web CSS, there is one big difference. The property names are different, and there is a lot of new properties specific to JavaFX. They are prefixed with <code class="language-text">-fx-</code>.</p> <p>Here are some examples:</p> <ul> <li><code class="language-text">-fx-background-color</code>: Background color</li> <li><code class="language-text">-fx-text-fill</code>: Text color</li> <li><code class="language-text">-fx-font-size</code>: Text size</li> </ul> <p>You can check the list of all the properties in the <a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html">official styling guide</a>. </p> <h2 id="pseudo-classes" style="position:relative;"><a href="#pseudo-classes" aria-label="pseudo classes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Pseudo-classes</h2> <p>In addition to regular classes, which mark specific components, there are so-called pseudo-classes, which mark a state of a component. This can be, for example, a class for marking that a component has the focus or there is the mouse cursor over the component.</p> <p>There's a whole bunch of built-in pseudo-classes. Let's take a look at Button. There are multiple pseudo-classes, you can use such as:</p> <ul> <li><code class="language-text">hover</code>: mouse is over the button</li> <li><code class="language-text">focused</code>: the button has the focus</li> <li><code class="language-text">disabled</code>: the button is disabled</li> <li><code class="language-text">pressed</code>: the button is pressed</li> </ul> <p>The pseudo-classes start with <code class="language-text">:</code> (e.g. <code class="language-text">:hover</code>) in the CSS selectors. You need, of course, specify to which component your pseudo class belongs - e.g. <code class="language-text">button:hover</code>. The following example shows a selector, which is applied for all buttons, which have focus:</p> <div class="gatsby-code-button-container" data-toaster-id="72593275219235996000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.button:focused { -fx-background-color: red; }`, `72593275219235996000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.button:focused</span> <span class="token punctuation">{</span> <span class="token property">-fx-background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Unlike in CSS, which has just basic pseudo-classes for states like focus and hover, JavaFX has component-specific pseudo-classes, which relate to different states or properties of components.</p> <p>For example:</p> <ul> <li>Scrollbars have <code class="language-text">horizontal</code> and <code class="language-text">vertical</code> pseudo-classes</li> <li>Cells have <code class="language-text">odd</code> and <code class="language-text">even</code></li> <li>TitledPane has <code class="language-text">expanded</code> and <code class="language-text">collapsed</code> </li> </ul> <h3 id="custom-pseudo-classes" style="position:relative;"><a href="#custom-pseudo-classes" aria-label="custom pseudo classes permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Custom pseudo-classes</h3> <p>In addition to build-in pseudo-classes, you can define and use your own.</p> <p>Let's create our custom Label (inheriting from Label class). It will have a new boolean property called <code class="language-text">shiny</code>. If it is true, we want our Label to have a <code class="language-text">shiny</code> pseudo-class.</p> <p>When the Label has the <code class="language-text">shiny</code> pseudo-class, we'll set its background to gold:</p> <div class="gatsby-code-button-container" data-toaster-id="58848072896141380000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`.shiny-label:shiny { -fx-background-color: gold; }`, `58848072896141380000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="css"><pre class="language-css"><code class="language-css"><span class="token selector">.shiny-label:shiny</span> <span class="token punctuation">{</span> <span class="token property">-fx-background-color</span><span class="token punctuation">:</span> gold<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre></div> <p>Now the class itself.</p> <div class="gatsby-code-button-container" data-toaster-id="31525750058534687000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`public class ShinyLabel extends Label { private BooleanProperty shiny; public ShinyLabel() { getStyleClass().add(&quot;shiny-label&quot;); shiny = new SimpleBooleanProperty(false); shiny.addListener(e -> { pseudoClassStateChanged(PseudoClass.getPseudoClass(&quot;shiny&quot;), shiny.get()); }); } public boolean isShiny() { return shiny.get(); } public void setShiny(boolean shiny) { this.shiny.set(shiny); } }`, `31525750058534687000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ShinyLabel</span> <span class="token keyword">extends</span> <span class="token class-name">Label</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">BooleanProperty</span> shiny<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">ShinyLabel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">getStyleClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"shiny-label"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> shiny <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SimpleBooleanProperty</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> shiny<span class="token punctuation">.</span><span class="token function">addListener</span><span class="token punctuation">(</span>e <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">pseudoClassStateChanged</span><span class="token punctuation">(</span><span class="token class-name">PseudoClass</span><span class="token punctuation">.</span><span class="token function">getPseudoClass</span><span class="token punctuation">(</span><span class="token string">"shiny"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> shiny<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isShiny</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> shiny<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setShiny</span><span class="token punctuation">(</span><span class="token keyword">boolean</span> shiny<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>shiny<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>shiny<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre></div> <p>There are several important parts here:</p> <ol> <li>We have our boolean property <code class="language-text">BooleanProperty</code> instead of regular <code class="language-text">boolean</code>. This means it is observable, and we can listen to changes in its value.</li> <li>We register a listener to be called every time the shiny value is changed using <code class="language-text">shiny.addListener()</code></li> <li>When the value changes, we add/remove the shiny pseudo-class depending on the current value <code class="language-text">pseudoClassStateChanged(PseudoClass.getPseudoClass(&quot;shiny&quot;), shiny.get())</code>.</li> <li>We add a custom class for all the labels <code class="language-text">shiny-label</code>, instead we would have just the <code class="language-text">label</code> class from our parent. This way, we can select only shiny labels.</li> </ol> <h2 id="default-stylesheet" style="position:relative;"><a href="#default-stylesheet" aria-label="default stylesheet permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Default stylesheet</h2> <p>Even if you don't provide any styles yourself, each JavaFX application already has some visual styling. There is a default stylesheet, which is applied to every application. It is called modena (since JavaFX 8, previously it used to be caspian).</p> <p>This stylesheet can be found in:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">jfxrt.jar\com\sun\javafx\scene\control\skin\modena\modena.css</code></pre></div> <p>Or you can check the file <a href="/0a0496c95957848317c311b088d2b62b/modena.css">directly here</a>. In the same directory, there is a whole bunch of images used by the stylesheet.</p> <p>This stylesheet provides the default styling but takes the lowest priority compared to other types of stylesheets, so you can easily override it.</p> <h2 id="scene-stylesheet" style="position:relative;"><a href="#scene-stylesheet" aria-label="scene stylesheet permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Scene stylesheet</h2> <p>In addition to the default stylesheet mentioned above, you can, of course, provide your own. The highest level on which you can apply styling is the whole scene. You can either provide that in your <a href="/javafx-fxml-scene-builder/">FXML</a>:</p> <div class="gatsby-code-button-container" data-toaster-id="75850297429604000000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<BorderPane xmlns=&quot;http://javafx.com/javafx&quot; xmlns:fx=&quot;http://javafx.com/fxml&quot; stylesheets=&quot;styles.css&quot; ... > ... </BorderPane>`, `75850297429604000000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>BorderPane</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://javafx.com/javafx<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">xmlns:</span>fx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://javafx.com/fxml<span class="token punctuation">"</span></span> <span class="token attr-name">stylesheets</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>styles.css<span class="token punctuation">"</span></span> <span class="token attr-name">...</span> <span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>BorderPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Or in your Java code:</p> <div class="gatsby-code-button-container" data-toaster-id="88063831113512010000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`String stylesheet = getClass().getResource(&quot;/styles.css&quot;).toExternalForm(); scene.getStylesheets().add(stylesheet);`, `88063831113512010000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">String</span> stylesheet <span class="token operator">=</span> <span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResource</span><span class="token punctuation">(</span><span class="token string">"/styles.css"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toExternalForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> scene<span class="token punctuation">.</span><span class="token function">getStylesheets</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>stylesheet<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Note the <code class="language-text">toExternalForm()</code> call. Scene expects stylesheet contents as a string, not the file, so we need to provide the contents of our stylesheet instead.</p> <h2 id="parent-stylesheet" style="position:relative;"><a href="#parent-stylesheet" aria-label="parent stylesheet permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Parent stylesheet</h2> <p>In addition to having a stylesheet for a whole scene, sometimes it may be useful to have styling on layout level. That is - for an individual container such as VBox, HBox, or GridPane. The common parent of all layouts is <code class="language-text">Parent</code> class, which defines methods for handling stylesheets on layout level. These styles apply only for the components in the given layout, not for the whole scene. Layout level styling takes precedence over scene level styling.</p> <div class="gatsby-code-button-container" data-toaster-id="43906104082868350000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<HBox stylesheets=&quot;styles.css&quot;> ... </HBox>`, `43906104082868350000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HBox</span> <span class="token attr-name">stylesheets</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>styles.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HBox</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java, you need to load the stylesheet contents yourself, same as previously with scene:</p> <div class="gatsby-code-button-container" data-toaster-id="34639804009819787000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`HBox box = new HBox(); String stylesheet = getClass().getResource(&quot;/styles.css&quot;).toExternalForm(); box.getStylesheets().add(stylesheet);`, `34639804009819787000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">HBox</span> box <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">String</span> stylesheet <span class="token operator">=</span> <span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResource</span><span class="token punctuation">(</span><span class="token string">"/styles.css"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toExternalForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> box<span class="token punctuation">.</span><span class="token function">getStylesheets</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>stylesheet<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="inline-styles" style="position:relative;"><a href="#inline-styles" aria-label="inline styles permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Inline styles</h2> <p>So far, we've covered only cases of assigning an external stylesheet to a whole scene or layout. But it is possible to set individual style properties on the component level.</p> <p>Here you don't have to bother with a selector as all the properties are set to a specific component.</p> <p>You can provide multiple properties separated by semicolon:</p> <div class="gatsby-code-button-container" data-toaster-id="10454050425302163000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<Label style=&quot;-fx-background-color: blue; -fx-text-fill: white&quot;> I'm feeling blue. </Label>`, `10454050425302163000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>-fx-background-color: blue; -fx-text-fill: white<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> I'm feeling blue. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java, you can use <code class="language-text">setStyle()</code> method:</p> <div class="gatsby-code-button-container" data-toaster-id="71971678226836870000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Label label = new Label(&quot;I'm feeling blue.&quot;); label.setStyle(&quot;-fx-background-color: blue; -fx-text-fill: white&quot;);`, `71971678226836870000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Label</span> label <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"I'm feeling blue."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> label<span class="token punctuation">.</span><span class="token function">setStyle</span><span class="token punctuation">(</span><span class="token string">"-fx-background-color: blue; -fx-text-fill: white"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Styling on component level takes precedence over both scene and parent (layout) styling.</p> <h3 id="why-to-avoid-them" style="position:relative;"><a href="#why-to-avoid-them" aria-label="why to avoid them permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Why to avoid them</h3> <p>Styling on component level may be convenient, but it is a quick and dirty solution. You give up the main advantage of CSS, which is separating styling from the styled components. You hardcode your visuals directly to the components now. You can no longer easily switch your stylesheets when needed, you cannot change themes.</p> <p>Moreover, you no longer have a single central place where your styling is defined. When you need to change something across a set of similar components, you need to modify each of the components individually instead of editing just one place in your external stylesheet. Inline styling components should be therefore avoided.</p> <h2 id="stylesheet-priorities" style="position:relative;"><a href="#stylesheet-priorities" aria-label="stylesheet priorities permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Stylesheet priorities</h2> <p>You can provide styling on multiple levels - scene, parent, inline styles, and there is a default modena stylesheet. If you change that same property of the same component on multiple levels, JavaFX has a priority setting, which resolves what styles should be used. The list of priorities is - from highest to lowest:</p> <ol> <li>Inline styles</li> <li>Parent styles</li> <li>Scene styles</li> <li>Default styles</li> </ol> <p>That means if you set the background color of a specific label both inline and on the scene level, JavaFX will use the value set in inline styles as it has higher priority.</p> <h2 id="further-reading" style="position:relative;"><a href="#further-reading" aria-label="further reading permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Further reading</h2> <p>There are numerous CSS properties in JavaFX, and describing them is beyond the scope of this post, for a detailed list, please see the official <a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html">JavaFX CSS Reference Guide</a>.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li><a href="/javafx-layouts-advanced/">JavaFX Tutorial: Advanced layouts</a></li> <li class="series-current">JavaFX Tutorial: CSS Styling</li> <li><a href="/javafx-spring-boot/">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</a></li> </ol> </div>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[JavaFX Tutorial: Advanced layouts]]>https://www.vojtechruzicka.com/javafx-layouts-advanced/https://www.vojtechruzicka.com/javafx-layouts-advanced/Fri, 04 Oct 2019 22:12:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQCAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABo01mhYiQ/8QAGxAAAgIDAQAAAAAAAAAAAAAAAQIAEQMEEiH/2gAIAQEAAQUC1jSg2rF+tJ+44qHH7//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AVf/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEv/aAAgBAgEBPwFD/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERECExMv/aAAgBAQAGPwLfMOQakgz0f//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESExQVGBwdH/2gAIAQEAAT8hRkPSGSahgFZogUlzPl9fY5hXxP/aAAwDAQACAAMAAAAQ0D//xAAWEQEBAQAAAAAAAAAAAAAAAAAhARD/2gAIAQMBAT8QoZ//xAAWEQEBAQAAAAAAAAAAAAAAAAAAEWH/2gAIAQIBAT8Q1D//xAAcEAEBAAEFAQAAAAAAAAAAAAABEQAhMUFRYfD/2gAIAQEAAT8QsI2oFF5kxg1W1xGIPNuQMQiK7uKg0Sk9HzzHww61ed5//9k='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/f61582232d05f49074bf64fe29ed70be/c54d4/ui-layout-advanced.webp 175w, /static/f61582232d05f49074bf64fe29ed70be/a3432/ui-layout-advanced.webp 350w, /static/f61582232d05f49074bf64fe29ed70be/426ac/ui-layout-advanced.webp 700w, /static/f61582232d05f49074bf64fe29ed70be/c139f/ui-layout-advanced.webp 1050w, /static/f61582232d05f49074bf64fe29ed70be/7f403/ui-layout-advanced.webp 1400w, /static/f61582232d05f49074bf64fe29ed70be/fad48/ui-layout-advanced.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/f61582232d05f49074bf64fe29ed70be/e52aa/ui-layout-advanced.jpg 175w, /static/f61582232d05f49074bf64fe29ed70be/70ebb/ui-layout-advanced.jpg 350w, /static/f61582232d05f49074bf64fe29ed70be/29d31/ui-layout-advanced.jpg 700w, /static/f61582232d05f49074bf64fe29ed70be/9ecec/ui-layout-advanced.jpg 1050w, /static/f61582232d05f49074bf64fe29ed70be/d165a/ui-layout-advanced.jpg 1400w, /static/f61582232d05f49074bf64fe29ed70be/b17f8/ui-layout-advanced.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/f61582232d05f49074bf64fe29ed70be/29d31/ui-layout-advanced.jpg" alt="JavaFX Advanced Layouts" title="JavaFX Advanced Layouts" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>How to organize and position your GUI components in JavaFX application using advanced layouts.</p> <p>In the previous article <a href="/javafx-layouts-basic/">we covered some basic JavaFX layouts</a>. Now it's time to go through the rest of the available layouts.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li class="series-current">JavaFX Tutorial: Advanced layouts</li> <li><a href="/javafx-css/">JavaFX Tutorial: CSS Styling</a></li> <li><a href="/javafx-spring-boot/">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</a></li> </ol> </div> <h2 id="anchorpane" style="position:relative;"><a href="#anchorpane" aria-label="anchorpane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>AnchorPane</h2> <p>AnchorPane is an interesting and powerful layout. It allows you to define anchor points to the components inside. There are 4 types of anchors:</p> <ul> <li>top</li> <li>bottom</li> <li>left</li> <li>right</li> </ul> <p>Each component can have any combination of anchors. From zero up to all four. Anchor point also defines a distance. </p> <p>Anchoring means that the component keeps the defined distance from a particular edge of the layout (e.g., TOP). This distance is preserved even on resize of the layout.</p> <p>For example: <code class="language-text">anchorRight=10</code> means that the component will keep distance 10 from the right edge of the layout.</p> <p>You can combine two anchor points, which are not in the opposite direction, to anchor your component to a specific corner of the layout.</p> <p>Anchor <code class="language-text">TOP = 10</code>, <code class="language-text">RIGHT = 10</code> means that the component will stay in the top right corner of the layout in the distance 10 from both edges.</p> <p><img src="/963c18c24f70c59fe397e365e7a4bc19/anchor-pane-corners.gif" alt="AnchorPane corner alignment"></p> <p>In the example above, the size of each component stays the same on resizing. However, if you define anchor points in the opposite directions, you can make your component grow/shrink on resizing.</p> <p><img src="/e47127c93c1213ac26837358ec8b21dd/anchor-pane-horizontal-resize.gif" alt="AnchorPane stretch"></p> <p>You can have various combinations like this:</p> <ul> <li>LEFT+RIGHT resizes horizontally</li> <li>TOP+BOTTOM resizes vertically</li> <li>All 4 anchors mean both horizontal and vertical resizing</li> </ul> <p>Defining anchor points in FXML is easy. In the following example, there are all four, but you can include none or only these which you want.</p> <div class="gatsby-code-button-container" data-toaster-id="91288294317661700000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<AnchorPane> <Button AnchorPane.topAnchor=&quot;10&quot; AnchorPane.leftAnchor=&quot;10&quot; AnchorPane.rightAnchor=&quot;10&quot; AnchorPane.bottomAnchor=&quot;10&quot;>I am fully anchored!</Button> </AnchorPane>`, `91288294317661700000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AnchorPane</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span> <span class="token attr-name">AnchorPane.topAnchor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">AnchorPane.leftAnchor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">AnchorPane.rightAnchor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">AnchorPane.bottomAnchor</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>I am fully anchored!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>AnchorPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Now let's look how anchoring is achieved in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="71095472828215260000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`AnchorPane anchorPane = new AnchorPane(); Button button = new Button(&quot;I am fully anchored!&quot;); AnchorPane.setTopAnchor(button, 10d); AnchorPane.setBottomAnchor(button, 10d); AnchorPane.setLeftAnchor(button, 10d); AnchorPane.setRightAnchor(button, 10d); anchorPane.getChildren().add(button);`, `71095472828215260000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">AnchorPane</span> anchorPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AnchorPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> button <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"I am fully anchored!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">AnchorPane</span><span class="token punctuation">.</span><span class="token function">setTopAnchor</span><span class="token punctuation">(</span>button<span class="token punctuation">,</span> <span class="token number">10d</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">AnchorPane</span><span class="token punctuation">.</span><span class="token function">setBottomAnchor</span><span class="token punctuation">(</span>button<span class="token punctuation">,</span> <span class="token number">10d</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">AnchorPane</span><span class="token punctuation">.</span><span class="token function">setLeftAnchor</span><span class="token punctuation">(</span>button<span class="token punctuation">,</span> <span class="token number">10d</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">AnchorPane</span><span class="token punctuation">.</span><span class="token function">setRightAnchor</span><span class="token punctuation">(</span>button<span class="token punctuation">,</span> <span class="token number">10d</span><span class="token punctuation">)</span><span class="token punctuation">;</span> anchorPane<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>button<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="gridpane" style="position:relative;"><a href="#gridpane" aria-label="gridpane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>GridPane</h2> <p>GridPane is a layout that allows you to organize your components into a grid. Unlike <a href="/javafx-layouts-basic/#tilepane">TilePane</a>, which is adding components one after another, here you need to specify the coordinates of the target location in your grid when adding a new component.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 41.142857142857146%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsSAAALEgHS3X78AAAA8klEQVQY03VQy04DMQzM//8ZQqBN4thJilDLQlkOdN9wZezQnmC0Gjm2MzNZ93I+vw/DxzB8Xi7f/+PLsO/7OI44LsuyrqsLx1Pp356Op9e+b0t/8rZtu+FiHvM8T9Pk7lJ95BIOzzUrUkqlKGcRooQOWLJYv4hIYm0ys0h2D5Hug86JSIRvjHGMEZwMrYPLIQRoYaSXszC+WisGtUKbmwMY21ZzsyoZmQonrkW5lOpMVSNpVIvdWDQ2tVr97QnwpEj2FpKcHRsgA7ZVzibUnJunsvlff8qvk4OG9x4b3ge0UF+ZbnUkClbrctdRos53yPsDeZeeLwCJCnQAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/29bc2592e616cc112f5ec510b36617aa/c54d4/grid-pane.webp 175w, /static/29bc2592e616cc112f5ec510b36617aa/a3432/grid-pane.webp 350w, /static/29bc2592e616cc112f5ec510b36617aa/426ac/grid-pane.webp 700w, /static/29bc2592e616cc112f5ec510b36617aa/ef2bd/grid-pane.webp 996w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/29bc2592e616cc112f5ec510b36617aa/4edbd/grid-pane.png 175w, /static/29bc2592e616cc112f5ec510b36617aa/13ae7/grid-pane.png 350w, /static/29bc2592e616cc112f5ec510b36617aa/8c557/grid-pane.png 700w, /static/29bc2592e616cc112f5ec510b36617aa/5caea/grid-pane.png 996w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/29bc2592e616cc112f5ec510b36617aa/8c557/grid-pane.png" alt="GridPane" title="GridPane" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <div class="gatsby-code-button-container" data-toaster-id="31192380026834022000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<GridPane hgap=&quot;10&quot; vgap=&quot;10&quot;> <Label GridPane.rowIndex=&quot;0&quot; GridPane.columnIndex=&quot;0&quot;>First</Label> ... </GridPane>`, `31192380026834022000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridPane</span> <span class="token attr-name">hgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">vgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name">GridPane.rowIndex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">GridPane.columnIndex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>First<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridPane</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java, when adding a new component, we specify first the ColumnIndex (x) and RowIndex(y).</p> <div class="gatsby-code-button-container" data-toaster-id="69539226849813400000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(` GridPane grid = new GridPane(); grid.add(new Label(&quot;Hello!&quot;), columnIndex, rowIndex);`, `69539226849813400000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"> <span class="token class-name">GridPane</span> grid <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GridPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> grid<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"Hello!"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> columnIndex<span class="token punctuation">,</span> rowIndex<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="spacing" style="position:relative;"><a href="#spacing" aria-label="spacing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Spacing</h2> <p>By default, grid cells have no spacing. The components are directly next to each other, with no margins. Spacing can be defined separately for rows and columns - that is: horizontally and vertically.</p> <ul> <li><code class="language-text">hgap</code> sets horizontal spacing (between columns)</li> <li><code class="language-text">vgap</code> sets vertical spacing (between rows)</li> </ul> <div class="gatsby-code-button-container" data-toaster-id="30594181231241360000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<GridPane hgap=&quot;10&quot; vgap=&quot;10&quot;> ... </GridPane>`, `30594181231241360000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridPane</span> <span class="token attr-name">hgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">vgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Spacing defined in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="53508202236279700000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`GridPane grid = new GridPane(); grid.setHgap(10); grid.setVgap(10);`, `53508202236279700000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">GridPane</span> grid <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GridPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> grid<span class="token punctuation">.</span><span class="token function">setHgap</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> grid<span class="token punctuation">.</span><span class="token function">setVgap</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="spanning-multiple-cells" style="position:relative;"><a href="#spanning-multiple-cells" aria-label="spanning multiple cells permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Spanning multiple cells</h3> <p>Components in GridPane can span multiple rows and/or columns.</p> <p>A component with rowspan is expanding to the bottom from its original cell. A component with colspan is expanding to the right.</p> <div class="gatsby-code-button-container" data-toaster-id="92777541825650980000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<GridPane> <Label GridPane.columnSpan=&quot;2&quot; GridPane.rowSpan=&quot;2&quot; GridPane.rowIndex=&quot;0&quot; GridPane.columnIndex=&quot;0&quot;> Foo! </Label> </GridPane>`, `92777541825650980000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridPane</span><span class="token punctuation">></span></span> <span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name">GridPane.columnSpan</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span></span><span class="gatsby-highlight-code-line"> <span class="token attr-name">GridPane.rowSpan</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span></span><span class="gatsby-highlight-code-line"> <span class="token attr-name">GridPane.rowIndex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> </span> <span class="token attr-name">GridPane.columnIndex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Foo! <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridPane</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java, there are two ways of setting row and column span. You can either set it directly when adding a component to the grid:</p> <div class="gatsby-code-button-container" data-toaster-id="95654293189950870000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`grid.add(component, columnIndex, rowIndex, columnSpan, rowSpan);`, `95654293189950870000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java">grid<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> columnIndex<span class="token punctuation">,</span> rowIndex<span class="token punctuation">,</span> columnSpan<span class="token punctuation">,</span> rowSpan<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Or through GridPane:</p> <div class="gatsby-code-button-container" data-toaster-id="13031221046124464000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`GridPane.setColumnSpan(component, columnSpan); GridPane.setRowSpan(component, rowSpan);`, `13031221046124464000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">GridPane</span><span class="token punctuation">.</span><span class="token function">setColumnSpan</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> columnSpan<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">GridPane</span><span class="token punctuation">.</span><span class="token function">setRowSpan</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> rowSpan<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="sizing" style="position:relative;"><a href="#sizing" aria-label="sizing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Sizing</h3> <p>Even though in the initial example, all the cells were the same size, it does not necessarily have to be the case. The sizing of grid cells is the following:</p> <ul> <li>Height of each row is equal to the highest element in the row</li> <li>Width of each column is equal to the widest element in the column</li> </ul> <h3 id="column-and-row-constraints" style="position:relative;"><a href="#column-and-row-constraints" aria-label="column and row constraints permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Column and row constraints</h3> <p>As mentioned, by default, the columns and rows are sized based on the components inside. Fortunately, you can have better control over how individual columns and rows are sized.</p> <p>There are <code class="language-text">ColumnContstraints</code> and <code class="language-text">RowConstraints</code> classes, which are used for this.</p> <p>You have basically two options. Either set percentage of available space for individual rows and columns, or you set preferred width/height. In the latter case you can also define preferred behavior when the columns and rows are resized.</p> <h4 id="percentage" style="position:relative;"><a href="#percentage" aria-label="percentage permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Percentage</h4> <p>This is quite straightforward. You can set a percentage of the available space to be occupied by the given row or column. When the layout is resized, the rows and columns are resized as well to match the new size.</p> <div class="gatsby-code-button-container" data-toaster-id="13628841248092870000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<GridPane> <columnConstraints> <ColumnConstraints percentWidth=&quot;50&quot; /> <ColumnConstraints percentWidth=&quot;50&quot; /> </columnConstraints> <rowConstraints> <RowConstraints percentHeight=&quot;50&quot; /> <RowConstraints percentHeight=&quot;50&quot; /> </rowConstraints> ... </GridPane>`, `13628841248092870000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridPane</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>columnConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ColumnConstraints</span> <span class="token attr-name">percentWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ColumnConstraints</span> <span class="token attr-name">percentWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>columnConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>rowConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>RowConstraints</span> <span class="token attr-name">percentHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>RowConstraints</span> <span class="token attr-name">percentHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>rowConstraints</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridPane</span><span class="token punctuation">></span></span></code></pre></div> <p>The same example in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="25601532968200004000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`GridPane gridPane = new GridPane(); ColumnConstraints col1 = new ColumnConstraints(); col1.setPercentWidth(50); ColumnConstraints col2 = new ColumnConstraints(); col2.setPercentWidth(50); gridPane.getColumnConstraints().addAll(col1, col2); RowConstraints row1 = new RowConstraints(); row1.setPercentHeight(50); RowConstraints row2 = new RowConstraints(); row2.setPercentHeight(50); gridPane.getRowConstraints().addAll(row1, row2);`, `25601532968200004000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">GridPane</span> gridPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GridPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ColumnConstraints</span> col1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col1<span class="token punctuation">.</span><span class="token function">setPercentWidth</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ColumnConstraints</span> col2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col2<span class="token punctuation">.</span><span class="token function">setPercentWidth</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> gridPane<span class="token punctuation">.</span><span class="token function">getColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>col1<span class="token punctuation">,</span> col2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">RowConstraints</span> row1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row1<span class="token punctuation">.</span><span class="token function">setPercentHeight</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">RowConstraints</span> row2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row2<span class="token punctuation">.</span><span class="token function">setPercentHeight</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> gridPane<span class="token punctuation">.</span><span class="token function">getRowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>row1<span class="token punctuation">,</span> row2<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h4 id="absolute-size" style="position:relative;"><a href="#absolute-size" aria-label="absolute size permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Absolute size</h4> <p>Instead of defining percentage size, you can define preferred and minimal size. In addition to that, you can specify how the row/column should behave on resize. Columns are using <code class="language-text">hgrow</code> property, and rows have <code class="language-text">vgrow</code>.</p> <p>These properties can have three distinct values.</p> <ul> <li><code class="language-text">NEVER</code>: Never grow or shrink on resizing. The default value.</li> <li><code class="language-text">ALWAYS</code>: When resized, all the elements with this value are either stretched to fill the available space or shrank.</li> <li><code class="language-text">SOMETIMES</code>: These elements are resized only if there are no other elements with <code class="language-text">ALWAYS</code>.</li> </ul> <div class="gatsby-code-button-container" data-toaster-id="96171708632367120000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<GridPane> <columnConstraints> <ColumnConstraints minWidth=&quot;50&quot; prefWidth=&quot;100&quot; /> <ColumnConstraints minWidth=&quot;50&quot; prefWidth=&quot;100&quot; hgrow=&quot;SOMETIMES&quot; /> </columnConstraints> <rowConstraints> <RowConstraints minHeight=&quot;50&quot; prefHeight=&quot;100&quot; /> <RowConstraints minHeight=&quot;50&quot; prefHeight=&quot;100&quot; vgrow=&quot;SOMETIMES&quot; /> </rowConstraints> ... </GridPane>`, `96171708632367120000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridPane</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>columnConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ColumnConstraints</span> <span class="token attr-name">minWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">prefWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ColumnConstraints</span> <span class="token attr-name">minWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">prefWidth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">hgrow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>SOMETIMES<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>columnConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>rowConstraints</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>RowConstraints</span> <span class="token attr-name">minHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">prefHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>RowConstraints</span> <span class="token attr-name">minHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">prefHeight</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">vgrow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>SOMETIMES<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>rowConstraints</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridPane</span><span class="token punctuation">></span></span></code></pre></div> <p>The same example in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="66803931180977290000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(` GridPane gridPane = new GridPane(); ColumnConstraints col1 = new ColumnConstraints(); col1.setMinWidth(50); col1.setPrefWidth(100); ColumnConstraints col2 = new ColumnConstraints(); col2.setMinWidth(50); col2.setPrefWidth(100); col2.setHgrow(Priority.SOMETIMES); gridPane.getColumnConstraints().addAll(col1, col2); RowConstraints row1 = new RowConstraints(); row1.setMinHeight(50); row1.setPrefHeight(100); RowConstraints row2 = new RowConstraints(); row2.setMinHeight(50); row2.setPrefHeight(100); row2.setVgrow(Priority.SOMETIMES); gridPane.getRowConstraints().addAll(row1, row2);`, `66803931180977290000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"> <span class="token class-name">GridPane</span> gridPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GridPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ColumnConstraints</span> col1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col1<span class="token punctuation">.</span><span class="token function">setMinWidth</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col1<span class="token punctuation">.</span><span class="token function">setPrefWidth</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ColumnConstraints</span> col2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col2<span class="token punctuation">.</span><span class="token function">setMinWidth</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col2<span class="token punctuation">.</span><span class="token function">setPrefWidth</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> col2<span class="token punctuation">.</span><span class="token function">setHgrow</span><span class="token punctuation">(</span><span class="token class-name">Priority</span><span class="token punctuation">.</span>SOMETIMES<span class="token punctuation">)</span><span class="token punctuation">;</span> gridPane<span class="token punctuation">.</span><span class="token function">getColumnConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>col1<span class="token punctuation">,</span> col2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">RowConstraints</span> row1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row1<span class="token punctuation">.</span><span class="token function">setMinHeight</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row1<span class="token punctuation">.</span><span class="token function">setPrefHeight</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">RowConstraints</span> row2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row2<span class="token punctuation">.</span><span class="token function">setMinHeight</span><span class="token punctuation">(</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row2<span class="token punctuation">.</span><span class="token function">setPrefHeight</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span> row2<span class="token punctuation">.</span><span class="token function">setVgrow</span><span class="token punctuation">(</span><span class="token class-name">Priority</span><span class="token punctuation">.</span>SOMETIMES<span class="token punctuation">)</span><span class="token punctuation">;</span> gridPane<span class="token punctuation">.</span><span class="token function">getRowConstraints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>row1<span class="token punctuation">,</span> row2<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>You can also specify <code class="language-text">maxHeight</code> and <code class="language-text">maxWidth</code> for individual rows and columns.</p> <h2 id="borderpane" style="position:relative;"><a href="#borderpane" aria-label="borderpane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>BorderPane</h2> <p>BorderPane is a layout with five sections:</p> <ul> <li>Top</li> <li>Bottom</li> <li>Right</li> <li>Left</li> <li>Center</li> </ul> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 50.28571428571429%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAABRklEQVQoz3VSa1OCUBDl//+avsRMD0xJYBTBsneOjSIgRk1Z0+MDcNp79wKi8WHnsrvnnLuHu1o2N5AvbYo+hdWMoA/EDtIrHelEl9+itofbCi1bdFCEjhIVIufIQ1vltuz9PPXwPesCFc5mjMBKoRqv5UEPRTQk4oCFA5PzaMBBdVCOWGHKOtUklzjM5bomrGLlA4nPILoRsUcxqiPhfqNGGIEVHKzHij+CVoQWfucO3h+6chJ5ARGKyK3Ot7szbERf5WVPYMVk6fUpPqYmDUaCIMGvmYWXG0Pa+k8wuTxCMjlWk+0I0hBLT8frbYcEPWU5GcuxWy2vud9q+fmitlw+St72KBRYuWqy3UcxmUPcvHyUeg8t3kW6oPqmM6O929yf4PPR2NpVdUpscy81tuhXNhp26Z9k4RBT+wAL91BaamK8vfgDniTZgXF5fzAAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/b64095193924b44e7cc6d66c630a1357/c54d4/borderpane.webp 175w, /static/b64095193924b44e7cc6d66c630a1357/a3432/borderpane.webp 350w, /static/b64095193924b44e7cc6d66c630a1357/426ac/borderpane.webp 700w, /static/b64095193924b44e7cc6d66c630a1357/20e4c/borderpane.webp 1042w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/b64095193924b44e7cc6d66c630a1357/4edbd/borderpane.png 175w, /static/b64095193924b44e7cc6d66c630a1357/13ae7/borderpane.png 350w, /static/b64095193924b44e7cc6d66c630a1357/8c557/borderpane.png 700w, /static/b64095193924b44e7cc6d66c630a1357/5819f/borderpane.png 1042w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/b64095193924b44e7cc6d66c630a1357/8c557/borderpane.png" alt="BorderPane" title="BorderPane" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>You can assign components to the individual sections of the BorderPane:</p> <div class="gatsby-code-button-container" data-toaster-id="21564429865917243000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<BorderPane> <top> <Label>TOP</Label> </top> <bottom> <Label>BOTTOM</Label> </bottom> <left> <Label>LEFT</Label> </left> <right> <Label>RIGHT</Label> </right> <center> <Label>CENTER</Label> </center> </BorderPane>`, `21564429865917243000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>BorderPane</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>top</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span><span class="token punctuation">></span></span>TOP<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>top</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>bottom</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span><span class="token punctuation">></span></span>BOTTOM<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>bottom</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>left</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span><span class="token punctuation">></span></span>LEFT<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>left</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>right</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span><span class="token punctuation">></span></span>RIGHT<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>right</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>center</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span><span class="token punctuation">></span></span>CENTER<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>center</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>BorderPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Now the same example in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="68786476142142040000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`Label top = new Label(&quot;TOP&quot;); Label bottom = new Label(&quot;BOTTOM&quot;); Label left = new Label(&quot;LEFT&quot;); Label right = new Label(&quot;RIGHT&quot;); Label center = new Label(&quot;CENTER&quot;); BorderPane borderPane = new BorderPane(); borderPane.setTop(top); borderPane.setBottom(bottom); borderPane.setLeft(left); borderPane.setRight(right); borderPane.setCenter(center);`, `68786476142142040000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">Label</span> top <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"TOP"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Label</span> bottom <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"BOTTOM"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Label</span> left <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"LEFT"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Label</span> right <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"RIGHT"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Label</span> center <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Label</span><span class="token punctuation">(</span><span class="token string">"CENTER"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">BorderPane</span> borderPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BorderPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> borderPane<span class="token punctuation">.</span><span class="token function">setTop</span><span class="token punctuation">(</span>top<span class="token punctuation">)</span><span class="token punctuation">;</span> borderPane<span class="token punctuation">.</span><span class="token function">setBottom</span><span class="token punctuation">(</span>bottom<span class="token punctuation">)</span><span class="token punctuation">;</span> borderPane<span class="token punctuation">.</span><span class="token function">setLeft</span><span class="token punctuation">(</span>left<span class="token punctuation">)</span><span class="token punctuation">;</span> borderPane<span class="token punctuation">.</span><span class="token function">setRight</span><span class="token punctuation">(</span>right<span class="token punctuation">)</span><span class="token punctuation">;</span> borderPane<span class="token punctuation">.</span><span class="token function">setCenter</span><span class="token punctuation">(</span>center<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="sizing-1" style="position:relative;"><a href="#sizing-1" aria-label="sizing 1 permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Sizing</h3> <p>All the regions except the center have a fixed size. The center then fills the rest of the space.</p> <p>Top and Bottom regions are stretched across all the available horizontal space. Their height is based on the height of the component inside.</p> <p>Left and right fill all the available vertical space (except what's occupied by top and bottom). Their width is dependent on the width of the component inside.</p> <p>The center has a dynamic size and fills the rest of the space not occupied by other sections. Let's look at an example:</p> <p><img src="/28c7d50d5bc6812af0160078067f0e22/borderpane-resizing.gif" alt="BorderPane Resizing"> </p> <h2 id="whats-next" style="position:relative;"><a href="#whats-next" aria-label="whats next permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>What's next</h2> <p>Now when we know how to use various layouts, we'll cover how to style JavaFX components using CSS.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li><a href="/javafx-layouts-basic/">JavaFX Tutorial: Basic layouts</a></li> <li class="series-current">JavaFX Tutorial: Advanced layouts</li> <li><a href="/javafx-css/">JavaFX Tutorial: CSS Styling</a></li> <li><a href="/javafx-spring-boot/">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</a></li> </ol> </div>https://www.vojtechruzicka.com/favicon.svghttps://www.vojtechruzicka.com/favicon.svg007accUA-76533683-1<![CDATA[JavaFX Tutorial: Basic layouts]]>https://www.vojtechruzicka.com/javafx-layouts-basic/https://www.vojtechruzicka.com/javafx-layouts-basic/Tue, 01 Oct 2019 22:40:03 GMT<p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 66.85714285714286%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEAv/EABYBAQEBAAAAAAAAAAAAAAAAAAECA//aAAwDAQACEAMQAAABtWxAwGi8f//EABsQAAICAwEAAAAAAAAAAAAAAAECERIAAyIy/9oACAEBAAEFAvYKCrBJvzIfNsX/AP/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAaEAACAgMAAAAAAAAAAAAAAAAAEQExAhAh/9oACAEBAAY/AnGJSLgaOxr/xAAdEAACAgIDAQAAAAAAAAAAAAAAEQExQVEhYXGB/9oACAEBAAE/IWzwPI7cN4ZhL4U6ijV2RbxR/9oADAMBAAIAAwAAABCT3//EABYRAAMAAAAAAAAAAAAAAAAAAAABIf/aAAgBAwEBPxB0dH//xAAVEQEBAAAAAAAAAAAAAAAAAAABAP/aAAgBAgEBPxAIb//EAB0QAQACAgIDAAAAAAAAAAAAAAEAESExQWFRccH/2gAIAQEAAT8Q6MlMOzE2g6NvbnUD4TQ41cMhoa4gwSnbK8IAADXyf//Z'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/d4506ff03b5fc3eec17f9302d6a97c0d/c54d4/ui-layout.webp 175w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/a3432/ui-layout.webp 350w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/426ac/ui-layout.webp 700w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/c139f/ui-layout.webp 1050w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/7f403/ui-layout.webp 1400w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/fad48/ui-layout.webp 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/d4506ff03b5fc3eec17f9302d6a97c0d/e52aa/ui-layout.jpg 175w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/70ebb/ui-layout.jpg 350w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/29d31/ui-layout.jpg 700w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/9ecec/ui-layout.jpg 1050w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/d165a/ui-layout.jpg 1400w, /static/d4506ff03b5fc3eec17f9302d6a97c0d/b17f8/ui-layout.jpg 1600w" sizes="(max-width: 700px) 100vw, 700px" type="image/jpeg" /> <img class="gatsby-resp-image-image" src="/static/d4506ff03b5fc3eec17f9302d6a97c0d/29d31/ui-layout.jpg" alt="JavaFX Layouts" title="JavaFX Layouts" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>How to organize and layout your GUI components in JavaFX application.</p> <p>This is the fourth article in the JavaFX series. In the previous article, I described <a href="/javafx-fxml-scene-builder/">how to use FXML and SceneBuilder</a> to create your user interface. Now we'll cover layouts.</p> <!--TODO remove this after they fix gatsby-remark-series --> <div class="series-table-of-content"> <div>All posts in the JavaFX series</div> <ol> <li><a href="/javafx-getting-started/">JavaFX Tutorial: Getting started</a></li> <li><a href="/javafx-hello-world/">JavaFX Tutorial: Hello world</a></li> <li><a href="/javafx-fxml-scene-builder/">JavaFX Tutorial: FXML and SceneBuilder</a></li> <li class="series-current">JavaFX Tutorial: Basic layouts</li> <li><a href="/javafx-layouts-advanced/">JavaFX Tutorial: Advanced layouts</a></li> <li><a href="/javafx-css/">JavaFX Tutorial: CSS Styling</a></li> <li><a href="/javafx-spring-boot/">JavaFX Weaver: Integration of JavaFX and Spring Boot applications</a></li> </ol> </div> <h2 id="layouts" style="position:relative;"><a href="#layouts" aria-label="layouts permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Layouts</h2> <p>Layouts, in a nutshell, are containers for components. This is useful as you can then position this container as a whole no matter what components are inside. Moreover, each scene can only hold one component, so you need a layout as a root component for your scene, so you can fit all the components you need. Of course, one layout is usually not enough, but you can nest layouts. That means you can put one layout inside another.</p> <p>In addition to that, layouts also organize and position your components inside. Based on the layout used, child components can be positioned for example:</p> <ul> <li>One after another horizontally</li> <li>One after another vertically</li> <li>One on top of each other as a stack</li> <li>In grid</li> </ul> <p>There are many more options. What's important is that a layout automatically updates the position of its children when it is resized. This way, you can have a consistent layout, even if your user resizes the application window.</p> <h2 id="hbox" style="position:relative;"><a href="#hbox" aria-label="hbox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>HBox</h2> <p>This is one of the most straightforward layouts available. It just puts all the items inside horizontally in a row, one after another, from left to right.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 27.999999999999996%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAAsSAAALEgHS3X78AAABFklEQVQY01VP207CQBTs/3+EH6A+GXlSoKVXeoEHRaPGINDdNsRYfVAS6e44u5VQHiZnz5w5s3Mc1URoP0Ko5oDIVsPpzwg/tYvMPUP9MgC+4p42Omp7u46uYug6gqlKcliN+Y4sUNNAhGieB9i9jdgn/zPylQdFnO77cCBm2K+nUJsMWqRoSxqKHO0mtYAoaOSzJtTkVqu5o0qfCKDLotsvc75jOPt1iif/Etu7G0AeDDOssmssp1f4XdFIeJzF2N7f4nFygd2SqZgGvOj7NcSDd473xZB9YhLO6Z7ZVMbIGEIW9kfDQ854tmd/10zbpZ53CUUAc6HhzEyXNFQysLd3mKCVw15vEJAb0dTFUWu4scUJR80fPSSz+icMPpUAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/85b6c192cace7c93dc032eb64c15f2cb/c54d4/hbox.webp 175w, /static/85b6c192cace7c93dc032eb64c15f2cb/a3432/hbox.webp 350w, /static/85b6c192cace7c93dc032eb64c15f2cb/426ac/hbox.webp 700w, /static/85b6c192cace7c93dc032eb64c15f2cb/c139f/hbox.webp 1050w, /static/85b6c192cace7c93dc032eb64c15f2cb/41bb6/hbox.webp 1222w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/85b6c192cace7c93dc032eb64c15f2cb/4edbd/hbox.png 175w, /static/85b6c192cace7c93dc032eb64c15f2cb/13ae7/hbox.png 350w, /static/85b6c192cace7c93dc032eb64c15f2cb/8c557/hbox.png 700w, /static/85b6c192cace7c93dc032eb64c15f2cb/e996b/hbox.png 1050w, /static/85b6c192cace7c93dc032eb64c15f2cb/636d3/hbox.png 1222w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/85b6c192cace7c93dc032eb64c15f2cb/8c557/hbox.png" alt="Horizontal Box" title="Horizontal Box" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>In FXML, you can use HBox like this:</p> <div class="gatsby-code-button-container" data-toaster-id="44497882045727870000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<HBox> <Button>1</Button> <Button>2</Button> <Button>3</Button> <Button>4</Button> </HBox>`, `44497882045727870000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HBox</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HBox</span><span class="token punctuation">></span></span></code></pre></div> <p>In Java you can use this:</p> <div class="gatsby-code-button-container" data-toaster-id="18260113991976135000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`HBox hbox = new HBox(); Button btn1 = new Button(&quot;1&quot;); Button btn2 = new Button(&quot;2&quot;); Button btn3 = new Button(&quot;3&quot;); Button btn4 = new Button(&quot;4&quot;); hbox.getChildren().addAll(btn1, btn2, btn3, btn4);`, `18260113991976135000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">HBox</span> hbox <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"3"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn4 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"4"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hbox<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>btn1<span class="token punctuation">,</span> btn2<span class="token punctuation">,</span> btn3<span class="token punctuation">,</span> btn4<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="spacing" style="position:relative;"><a href="#spacing" aria-label="spacing permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Spacing</h3> <p>Our elements are now neatly laid out horizontally in a row, one after another:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 251px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 32.57142857142857%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAABRUlEQVQY0z2Q3UvDMBTF+///A4q+qSCiE3Ubsun2IsqmaLu1WdfaNh9NmjSpSz+eB14q8/dwuAcu59zEyapmJa2vak9a8tN2TbP7w9q2bZuDtdZ2XVfXNQz/1jny5TjVD6kZJeZkxbckr7TWxpRlSQjhnOsesBhjIQQM2mgpC6WUM/qW+9rInO3b3WNIfVpYU2YYQ3qSJJzDtsKEQGccR1JK6IUIU1WgzgBxxfDtcCy4uFtnXkoFxZPphDIO21COs3Q4GitVbjabopDvi9er64FUmjHmDNa4FnS5WCpG7t3YTQjyvqbTJwhGCKVpGm3D2XwOJ/jrNWS5nx/PszmljFLqHC/jVRgtUOyF8ekbevFQBo09nucFQRAdcF0X4pIeuAJynctQngf8IhBnAb/ZylwUQsA3cWguekTvQeHBoHme8x5jzC9KZmtVMRBq9QAAAABJRU5ErkJggg=='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/07ebae91bc677625770b47ddb28a76a5/c54d4/hbox-no-spacing.webp 175w, /static/07ebae91bc677625770b47ddb28a76a5/ceeba/hbox-no-spacing.webp 251w" sizes="(max-width: 251px) 100vw, 251px" type="image/webp" /> <source srcset="/static/07ebae91bc677625770b47ddb28a76a5/4edbd/hbox-no-spacing.png 175w, /static/07ebae91bc677625770b47ddb28a76a5/26abe/hbox-no-spacing.png 251w" sizes="(max-width: 251px) 100vw, 251px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/07ebae91bc677625770b47ddb28a76a5/26abe/hbox-no-spacing.png" alt="HBox without spacing" title="HBox without spacing" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>However, it does not look very good as they are right after each other with no spacing. Fortunately, we can define the spacing between components using <code class="language-text">spacing</code> property of the HBox:</p> <div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">&lt;HBox spacing=&quot;10&quot;&gt; ... &lt;/HBox&gt;</code></pre></div> <p>Or in Java using <code class="language-text">setSpacing()</code>:</p> <div class="gatsby-code-button-container" data-toaster-id="99828269518372490000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`HBox hbox = new HBox(); hbox.setSpacing(10);`, `99828269518372490000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">HBox</span> hbox <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hbox<span class="token punctuation">.</span><span class="token function">setSpacing</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="padding" style="position:relative;"><a href="#padding" aria-label="padding permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Padding</h3> <p>The elements are now spaced properly, however, there is still no padding between elements and the HBox itself. It can be useful to add padding to our HBox:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 24.571428571428573%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsSAAALEgHS3X78AAABBUlEQVQY01WP0U6DQBBF+f8/MtUK3cIqiS3YRtMHC1IoQSiwgBZk4XbYpiY+nMzcO7Mzs9r4vQPqV2IDceTIDyb60lHeIFzlN4mN+ENX3li5CjSbv/zaRzOaLbQuX6H9YhhChs4nPAPn+BF9zCADEwMtaEMdVTBTeU+eDJdoEx0yYkpPfhcbaDMLWhFxZN6MChzpdg55sFGQ/gkYxI6hfDcoX6D079F6T8iop6PebH9H0VJvznuOyp8j9R+gTaejtjEel+hp0+/nAuPJBFJOV1sKpERBOrIUY0x1wVW8ecg5ffkFmqzfIMWKhrooIxMnuqxJnjFUDrG+IpwrSk/5zVv/65HEBYlXblhyt6DCAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/85a9b374b60abc0726b85f432e5dd8aa/c54d4/hbox-padding-spacing.webp 175w, /static/85a9b374b60abc0726b85f432e5dd8aa/a3432/hbox-padding-spacing.webp 350w, /static/85a9b374b60abc0726b85f432e5dd8aa/426ac/hbox-padding-spacing.webp 700w, /static/85a9b374b60abc0726b85f432e5dd8aa/c139f/hbox-padding-spacing.webp 1050w, /static/85a9b374b60abc0726b85f432e5dd8aa/7f403/hbox-padding-spacing.webp 1400w, /static/85a9b374b60abc0726b85f432e5dd8aa/950a9/hbox-padding-spacing.webp 1776w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/85a9b374b60abc0726b85f432e5dd8aa/4edbd/hbox-padding-spacing.png 175w, /static/85a9b374b60abc0726b85f432e5dd8aa/13ae7/hbox-padding-spacing.png 350w, /static/85a9b374b60abc0726b85f432e5dd8aa/8c557/hbox-padding-spacing.png 700w, /static/85a9b374b60abc0726b85f432e5dd8aa/e996b/hbox-padding-spacing.png 1050w, /static/85a9b374b60abc0726b85f432e5dd8aa/2cefc/hbox-padding-spacing.png 1400w, /static/85a9b374b60abc0726b85f432e5dd8aa/2ed34/hbox-padding-spacing.png 1776w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/85a9b374b60abc0726b85f432e5dd8aa/8c557/hbox-padding-spacing.png" alt="HBox with padding and spacing" title="HBox with padding and spacing" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>You can define each section of the padding separately - top, bottom, left and right.</p> <div class="gatsby-code-button-container" data-toaster-id="81646954947934240000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<HBox> <padding> <Insets top=&quot;10&quot; bottom=&quot;10&quot; left=&quot;10&quot; right=&quot;10&quot;/> </padding> ... </HBox>`, `81646954947934240000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HBox</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>padding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Insets</span> <span class="token attr-name">top</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">bottom</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">left</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">right</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>padding</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HBox</span><span class="token punctuation">></span></span></code></pre></div> <p>The same thing can be achieved in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="39953533533061820000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`HBox hbox = new HBox(); hbox.setPadding(new Insets(10, 10, 10, 10));`, `39953533533061820000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">HBox</span> hbox <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> hbox<span class="token punctuation">.</span><span class="token function">setPadding</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Insets</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="vbox" style="position:relative;"><a href="#vbox" aria-label="vbox permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>VBox</h2> <p>VBox is very similar to HBox, but instead of displaying the components inside horizontally in a row, it displays them vertically in a column:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 442px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 145.14285714285714%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAdCAYAAACqhkzFAAAACXBIWXMAAAsSAAALEgHS3X78AAADOElEQVRIx6WV208TQRTG+9fz4OVBEwMmisYEJRAujbQF2kLvLZRuC2JEBaHdS6EUpAKW7u3zzMyydrpbefDhl73Mt2dnznfOTKTfTsC6yMHqZoishHmeAXpF4LYi+FUKaEaJsGC4Vki8Q9R83B4939ZRL75HbP4ZlPwMDmqLkiaMiNWlgDd1ClqTcJngbhcHyiLy69PIJ6exX533xpWA/n5SkcHZGux2EpZBtGVMYx24oGVf5Ykcvx/V+BhrcC4LiPS1JThqAa5aIco+DrtqZUAbeq+VJc3f91uwm/SzThKROy0KV98GDJqyUZNw9R38/lHk9I9LdC3xd7KOntsKHK0Eu5NmAZdJVOEDrl71wWmNAhSwNP0Es1MTWHj1CJXYW9ydlCjAiJYCs1UOBRwzQ8JSt2G2tjjB2d1T92aYooB6lB4ohyxXWjkA2Oz5CmhcL4dqXJ1y2MqKGZod5lKCHI0R8RBiI8TH6pyfJTFDu5kiNomNUCyPceN2ixw+WfOWTDmElENFvj9rAOd7gtM6dzSgaw/n0HOZJVxyjpzsHxeRWXiJxMxzbMxPopp4h8svG7wCmJFjXI7ypPo1xcqH1xZzeAtHlSi+l5ZwWF7GSXUFt0cFEdDTClgdFkVA1imiIyiQVpUA+3uHLbkhrmd18cOAjmbYyotOGZyuwtTIPW0llIH6UcJUw3T0PY05l3kK2I7D1ql0tFQoppqUCNPYeprSQy5fZMWS+dKM+pBzhK6Id9192mX2xbXziXLb8MaGafBNwpJcNoIum80y9tIf8Dkz5xtzc5jjhj3QyyGbAwUc0EbANoTk7AsqmykevPc145XNmIDcZV42CndLgpVSZ5eW+0kUNityKmw3oKPCVoveko0VSiglW02F0yIzPNh9mNZWyZTWKuzupjgCLINKR0+MIT5EuMbydP4R4FIfuto238olWI+3dwRGVaCH6apU2Llhl+XWu8chYe9bFteHed5y7ChwWGeM6KTWe+gIWH7zFLOTE1h6/RjpuUkqxSSZo/y7bGCEHwH3ONTnHL4j1YJaf/vyAjq0fbM/OGo+BHbEChyfUQ0tt5kW+6FJUe3zTR79f3GvyvgDM1cyzqDPe4MAAAAASUVORK5CYII='); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/fb4f697d0246c5f2e1ff6d2429754a68/c54d4/vbox.webp 175w, /static/fb4f697d0246c5f2e1ff6d2429754a68/a3432/vbox.webp 350w, /static/fb4f697d0246c5f2e1ff6d2429754a68/3fa5c/vbox.webp 442w" sizes="(max-width: 442px) 100vw, 442px" type="image/webp" /> <source srcset="/static/fb4f697d0246c5f2e1ff6d2429754a68/4edbd/vbox.png 175w, /static/fb4f697d0246c5f2e1ff6d2429754a68/13ae7/vbox.png 350w, /static/fb4f697d0246c5f2e1ff6d2429754a68/e03bf/vbox.png 442w" sizes="(max-width: 442px) 100vw, 442px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/fb4f697d0246c5f2e1ff6d2429754a68/e03bf/vbox.png" alt="VBox" title="VBox" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>You can still set the spacing and padding in the same way as with HBox.</p> <p>In code, VBox is used exactly in the same way as HBox, just the name is different:</p> <div class="gatsby-code-button-container" data-toaster-id="58703076671663145000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<VBox spacing=&quot;10&quot;> <padding> <Insets top=&quot;10&quot; bottom=&quot;10&quot; left=&quot;10&quot; right=&quot;10&quot;/> </padding> <Button>1</Button> <Button>2</Button> <Button>3</Button> <Button>4</Button> </VBox>`, `58703076671663145000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>VBox</span> <span class="token attr-name">spacing</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>padding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Insets</span> <span class="token attr-name">top</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">bottom</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">left</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">right</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>padding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>4<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>VBox</span><span class="token punctuation">></span></span></code></pre></div> <p>And in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="14395647400114498000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`VBox vbox = new VBox(); vbox.setPadding(new Insets(10, 10, 10, 10)); vbox.setSpacing(10); Button btn1 = new Button(&quot;1&quot;); Button btn2 = new Button(&quot;2&quot;); Button btn3 = new Button(&quot;3&quot;); Button btn4 = new Button(&quot;4&quot;); vbox.getChildren().addAll(btn1, btn2, btn3, btn4);`, `14395647400114498000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">VBox</span> vbox <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">VBox</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> vbox<span class="token punctuation">.</span><span class="token function">setPadding</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Insets</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> vbox<span class="token punctuation">.</span><span class="token function">setSpacing</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"3"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn4 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"4"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> vbox<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>btn1<span class="token punctuation">,</span> btn2<span class="token punctuation">,</span> btn3<span class="token punctuation">,</span> btn4<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="stackpane" style="position:relative;"><a href="#stackpane" aria-label="stackpane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>StackPane</h2> <p>This layout is useful for stacking its components one on top of each other. The order of insertion determines the order of the items. That means the first item is on the bottom, the next is on top of it and so on.</p> <p>This can be useful, for example, for having an image and then having some text or button on top of it.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 665px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 65.14285714285714%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAADJElEQVQozwEZA+b8AJiflMO/qOrIp8ypirqegqqJc2xjWt60k6mGcNrBm6CMdpF6av7x0rS3mn2Hef3w0cnIsqOXgLCZg2peXQBaSURLTUtpX1Z1a2HZroviwpxpY122kXWZeGqNgXDNuZWCioLo4MLf17eVmYfb1LqXlIWci3dXVlVdUE4A47KXlXJfS0tIPUBAZFdSkYh4s66XjX5wwJ2BY2dlopeAjY1+qKGQ++7TxcSswcGrfH1vRktNW1VOoIhyANu+mezYtODOp8KhfquZg56fkIaEeJeKep6MdrOUe3NqZH+KgFxhYJOOf5KRgnVrYmNhXEhKTLWTdoJyZgCmgme+pIbszKHy2a/x3r/18NqoppV1Z1tTUE5fUEx2Vk9WTU6BfXV+eXBBRUkyMzpJR0pmWllVS05HS08ArG9ciFJLuHJcxph+2qqL6s6u//XDurGSi5iVfYeIbHl+VWt2NkZQQ0NFOT9GIiYuMzc9SEZKcWhicmplAIheVKl8aKBqXoZeVY1yZYF1ZqOIarK3rt7j5djb28zP0eDn6m+GkwsQGSUpMh8kLGxmXsqtkJuOf2dnaABxcWuMamB3Yl1wZmSwm4azqo6FgHZ+jY+LoqmerbC6w7zN2NCEkY8fIiopLzcgJC15b2P406u/p5JmaGkAZFhZUEhLWlxeW1pde3ZxuaaSsKaUlJGGkY6F17qc+das//LGrqSRMDY8JywzKzE8NTQ4rpJ/emljQj9DACsuNzA2PDM2Pi81PUdOVGZoaXBwcJGJhKiZj7ifjsqtlM2zl2ReXCsuNCcrMTc6QSAiKSwqMCkpMCIiKQAmKjMjJi4kJi4wMTg9PkNHQ0ddU1V4YmCAY16EZWB7Y15qVVRkTEtcQUBWPj0+MjQxKi01Kiw2KSouJyoAICMpHR8mJyUqLy4yOTM2TD9BVEJCY0dFaEhFZUlGYktIZE1KYkZDWjw7SjU2NiotOystMiUmKCEiKCEkAB8gJRwcIR0dIh0cIigjJy4nKi8oLC8nLDgrLS0nKzYuMjUrMEIxM0YzNCQiJyEeIigiJiMfIhwaHRsZHeaeU2InDVSdAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/45912e310c1e2602adc51733e13c7fed/c54d4/stack-pane.webp 175w, /static/45912e310c1e2602adc51733e13c7fed/a3432/stack-pane.webp 350w, /static/45912e310c1e2602adc51733e13c7fed/ced6d/stack-pane.webp 665w" sizes="(max-width: 665px) 100vw, 665px" type="image/webp" /> <source srcset="/static/45912e310c1e2602adc51733e13c7fed/4edbd/stack-pane.png 175w, /static/45912e310c1e2602adc51733e13c7fed/13ae7/stack-pane.png 350w, /static/45912e310c1e2602adc51733e13c7fed/5f4af/stack-pane.png 665w" sizes="(max-width: 665px) 100vw, 665px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/45912e310c1e2602adc51733e13c7fed/5f4af/stack-pane.png" alt="Stack Pane" title="Stack Pane" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>The following example shows StackPane in FXML:</p> <div class="gatsby-code-button-container" data-toaster-id="72160817534355220000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<StackPane> <ImageView> <Image url=&quot;/image.jpg&quot;/> </ImageView> <Button>Click Me!</Button> </StackPane>`, `72160817534355220000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>StackPane</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ImageView</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Image</span> <span class="token attr-name">url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/image.jpg<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ImageView</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span>Click Me!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>StackPane</span><span class="token punctuation">></span></span></code></pre></div> <p>The same example in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="34680250540747837000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`StackPane stackPane = new StackPane(); Image image = new Image(getClass().getResourceAsStream(&quot;/image.jpg&quot;)); ImageView imageView = new ImageView(image); Button btn = new Button(&quot;Click Me!&quot;); stackPane.getChildren().addAll(imageView, btn);`, `34680250540747837000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">StackPane</span> stackPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StackPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Image</span> image <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Image</span><span class="token punctuation">(</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResourceAsStream</span><span class="token punctuation">(</span><span class="token string">"/image.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ImageView</span> imageView <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ImageView</span><span class="token punctuation">(</span>image<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"Click Me!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> stackPane<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>imageView<span class="token punctuation">,</span> btn<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="item-alignment" style="position:relative;"><a href="#item-alignment" aria-label="item alignment permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Item alignment</h3> <p>You can set alignment of the items in the stack to better adjust their positioning:</p> <div class="gatsby-code-button-container" data-toaster-id="98149673523380600000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<StackPane alignment=&quot;BOTTOM_CENTER&quot;> ... </StackPane>`, `98149673523380600000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>StackPane</span> <span class="token attr-name">alignment</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>BOTTOM_CENTER<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>StackPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Of course, you can do the same in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="30730947446958320000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`StackPane stackPane = new StackPane(); stackPane.setAlignment(Pos.BOTTOM_CENTER);`, `30730947446958320000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">StackPane</span> stackPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StackPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> stackPane<span class="token punctuation">.</span><span class="token function">setAlignment</span><span class="token punctuation">(</span><span class="token class-name">Pos</span><span class="token punctuation">.</span>BOTTOM_CENTER<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h3 id="margin" style="position:relative;"><a href="#margin" aria-label="margin permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Margin</h3> <p>If you want even more fine-grained control of the positioning, you can set margin for individual items in the stack:</p> <div class="gatsby-code-button-container" data-toaster-id="80697007862217970000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<StackPane alignment=&quot;BOTTOM_CENTER&quot;> <ImageView> <Image url=&quot;/image.jpg&quot;/> </ImageView> <Button> <StackPane.margin> <Insets bottom=&quot;10&quot;/> </StackPane.margin> Click Me! </Button> </StackPane>`, `80697007862217970000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight has-highlighted-lines" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>StackPane</span> <span class="token attr-name">alignment</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>BOTTOM_CENTER<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ImageView</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Image</span> <span class="token attr-name">url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/image.jpg<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ImageView</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Button</span><span class="token punctuation">></span></span> <span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>StackPane.margin</span><span class="token punctuation">></span></span></span><span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Insets</span> <span class="token attr-name">bottom</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></span><span class="gatsby-highlight-code-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>StackPane.margin</span><span class="token punctuation">></span></span></span> Click Me! <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Button</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>StackPane</span><span class="token punctuation">></span></span></code></pre></div> <p>Or in Java:</p> <div class="gatsby-code-button-container" data-toaster-id="46868183105377485000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`StackPane stackPane = new StackPane(); Button btn = new Button(&quot;Click Me!&quot;); stackPane.getChildren().add(btn); StackPane.setMargin(btn, new Insets(0,0,10,0));`, `46868183105377485000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">StackPane</span> stackPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StackPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Button</span> btn <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span><span class="token string">"Click Me!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> stackPane<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>btn<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">StackPane</span><span class="token punctuation">.</span><span class="token function">setMargin</span><span class="token punctuation">(</span>btn<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">Insets</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">10</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <h2 id="flowpane" style="position:relative;"><a href="#flowpane" aria-label="flowpane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>FlowPane</h2> <p>Flow Pane can work in two modes - either horizontal (the default) or vertical.</p> <p>In horizontal mode, the items are displayed horizontally, one after another, similar to HBox. The difference is, once there is no more horizontal space available, it wraps to the next row under the first one and continues again. This way there can be many rows, not just one as with HBox.</p> <p>The vertical mode is very similar, but (like VBox) it displays items vertically, from top to bottom. Once there is no more space, it adds another column and continues.</p> <p>The following image illustrates these two modes:</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 43.42857142857143%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAACToAAAk6AGCYwUcAAABLElEQVQoz02R626EIBSEff9na5rtDwUEF7VcFJtuvLVabx3BtDtRcuRj5oBE4zi2XffthfrLaxiGdV2P4wBCHWgoRq9930Ej45ys6nVZsLrv+x+vtm2naQIWSpnPx7aevOs6FPCjWJblNJcfD/Juc1NV1lhrjTlHrfU8z8DcOtDSWKM1kFJKe4V9RXnVvBAep5yneDFcQn9gUqhXyhkXmGF4GKOU5nl+mRvnWMqllJnIMiHu97sQ4s+MJsjMsizkCs7Zs9k5h0QYuBeWIh5fwYx9AkiJRC7ORQJU/nduGkSGbMinpE9mjclAQwOYi6K4zHVdx3ECPw5DCKGU+ZGGH1aUJWqclXhhz3EcI+syowNuBX8/SRIAKXPYcB/btgEDocbNA95ubyjCTLjnX7dX7Axx6NAIAAAAAElFTkSuQmCC'); background-size: cover; display: block;" ></span> <picture> <source srcset="/static/d43143028edaa00a0b489ed3c1612ab4/c54d4/flow-pane-comparison.webp 175w, /static/d43143028edaa00a0b489ed3c1612ab4/a3432/flow-pane-comparison.webp 350w, /static/d43143028edaa00a0b489ed3c1612ab4/426ac/flow-pane-comparison.webp 700w, /static/d43143028edaa00a0b489ed3c1612ab4/c139f/flow-pane-comparison.webp 1050w, /static/d43143028edaa00a0b489ed3c1612ab4/f9357/flow-pane-comparison.webp 1110w" sizes="(max-width: 700px) 100vw, 700px" type="image/webp" /> <source srcset="/static/d43143028edaa00a0b489ed3c1612ab4/4edbd/flow-pane-comparison.png 175w, /static/d43143028edaa00a0b489ed3c1612ab4/13ae7/flow-pane-comparison.png 350w, /static/d43143028edaa00a0b489ed3c1612ab4/8c557/flow-pane-comparison.png 700w, /static/d43143028edaa00a0b489ed3c1612ab4/e996b/flow-pane-comparison.png 1050w, /static/d43143028edaa00a0b489ed3c1612ab4/73caa/flow-pane-comparison.png 1110w" sizes="(max-width: 700px) 100vw, 700px" type="image/png" /> <img class="gatsby-resp-image-image" src="/static/d43143028edaa00a0b489ed3c1612ab4/8c557/flow-pane-comparison.png" alt="Flow pane horizontal vs vertical" title="Flow pane horizontal vs vertical" loading="lazy" style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;" /> </picture> </span></p> <p>Note that items don't necessarily have to have the same size as they do have in the image above.</p> <p>Note how the position of the components is recalculated if you change the size of the container:</p> <p><img src="/852ceb58e4d3885c1a66d70785736e4e/flowpane-resizing.gif" alt="FlowPane Resizing"></p> <p>You can set the internal padding of this layout in the same way as for HBox and VBox. Spacing is a little bit different though. Instead of just one property for spacing, you need to have separate horizontal and vertical spacing as the items can be rendered in multiple rows/columns. For horizontal spacing use <code class="language-text">hgap</code>, for vertical spacing use <code class="language-text">vgap</code> instead.</p> <div class="gatsby-code-button-container" data-toaster-id="85946389703250850000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`FlowPane flowPane = new FlowPane(); flowPane.setOrientation(Orientation.VERTICAL); flowPane.setVgap(10); flowPane.setHgap(10); flowPane.getChildren().addAll(...);`, `85946389703250850000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="java"><pre class="language-java"><code class="language-java"><span class="token class-name">FlowPane</span> flowPane <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FlowPane</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> flowPane<span class="token punctuation">.</span><span class="token function">setOrientation</span><span class="token punctuation">(</span><span class="token class-name">Orientation</span><span class="token punctuation">.</span>VERTICAL<span class="token punctuation">)</span><span class="token punctuation">;</span> flowPane<span class="token punctuation">.</span><span class="token function">setVgap</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> flowPane<span class="token punctuation">.</span><span class="token function">setHgap</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span> flowPane<span class="token punctuation">.</span><span class="token function">getChildren</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre></div> <p>Example of FlowPane in FXML:</p> <div class="gatsby-code-button-container" data-toaster-id="3106595554908975000" data-toaster-class="gatsby-code-button-toaster" data-toaster-text-class="gatsby-code-button-toaster-text" data-toaster-text="Copied to clipboard." data-toaster-duration="3500" onClick="copyToClipboard(`<FlowPane hgap=&quot;10&quot; vgap=&quot;10&quot; orientation=&quot;VERTICAL&quot;> ... </FlowPane>`, `3106595554908975000`)" > <div class="gatsby-code-button" data-tooltip="Copy to clipboard" > <svg class="gatsby-code-button-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z"/></svg> </div> </div> <div class="gatsby-highlight" data-language="xml"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FlowPane</span> <span class="token attr-name">hgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">vgap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">orientation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>VERTICAL<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FlowPane</span><span class="token punctuation">></span></span></code></pre></div> <h2 id="tilepane" style="position:relative;"><a href="#tilepane" aria-label="tilepane permalink" class="anchor before"><svg aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>TilePane</h2> <p>This layout is very similar to FlowPane. The way it displays its components is almost identical. You can still have either horizontal or vertical mode and define <code class="language-text">vgap</code> and <code class="language-text">hgap</code>.</p> <p>One crucial difference is in the sizing of the cells. FlowPane assigns only space necessary for each component. TilePane, on the other hand, makes the size of all cells the same depending on the biggest item. This way, all the controls are nicely aligned in rows/columns.</p> <p><span class="gatsby-resp-image-wrapper" style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 700px; " > <span class="gatsby-resp-image-background-image" style="padding-bottom: 24.571428571428573%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAwElE